Educational Codeforces Round 84 (Rated for Div. 2) 比赛人数13522
[codeforces 1327E] Count The Blocks 打表找规律+根据规律找公式+优化公式
总目录详见https://blog.csdn.net/mrcrack/article/details/103564004
也在线测评地址https://codeforces.ml/contest/1327/problem/E
Problem | Lang | Verdict | Time | Memory |
---|---|---|---|---|
E - Count The Blocks | GNU C++11 | Accepted | 78 ms | 6100 KB |
首次能在赛后独立AC掉E题,可喜可贺。
以下为AC代码,若读者看不懂,请慢慢看代码的由来
#include <stdio.h>
#define maxn 200010
#define LL long long
#define mod 998244353
LL a[maxn],pow[maxn],sum[maxn],A[maxn];
int main(){
int n,i,j;
scanf("%d",&n);
pow[0]=1;
for(i=1;i<=n;i++)pow[i]=pow[i-1]*10%mod;
a[1]=10,sum[1]=10,A[1]=0;
for(i=2;i<=n;i++){
a[i]=i*pow[i]%mod;
a[i]=((a[i]-sum[i-1]*i+A[i-1])%mod+mod)%mod;
sum[i]=(sum[i-1]+a[i])%mod,A[i]=(A[i-1]+a[i]*(i-1)%mod)%mod;
}
for(i=n;i>=2;i--)printf("%lld ",a[i]);
printf("%lld\n",a[1]);
return 0;
}
拿到题目,想着就是排列组合的问题,n=1,n=2一想就通,n=3,n=4,已经想不清楚了,成千上万的数据啊
没辙,而且空想的规律未必对,采用暴力打表,先看看数据是怎么样的,再从排列组合入手。
以下为打表代码
#include <stdio.h>
#define LL long long
LL cnt[15],l,r;//cnt[1]长度为1的块的数量,cnt[2]长度为2的块的数量
int n,st[15],a[15];
void count(int x){//统计x中不同块的数量
int top=0,tot=1,i;
while(x){//将x中,每一位上的数字取出
st[++top]=x%10;
x/=10;
}
for(i=1;i<=n;i++)a[i]=0;//将n位数字,先设置为0
a[n+1]=-1;//边界设置
for(i=1;i<=top;i++)a[i]=st[i];//将分解出来的数字,复制到数组a中
for(i=2;i<=n+1;i++)
if(a[i]!=a[i-1]){
cnt[tot]++;//统计x中不同块的数量
tot=1;
}else tot++;
}
int main(){
int i;
scanf("%d",&n);
l=0,r=1;
for(i=1;i<=n;i++)r=r*10;
r--;//数值范围的有边界。
for(i=0;i<=r;i++)
count(i);
for(i=1;i<n;i++)printf("%lld ",cnt[i]);
printf("%lld\n",cnt[n]);
return 0;
}
由打表代码获得的几组关键数据如下
1
10
2
180 10
3
2610 180 10
4
34200 2610 180 10
5
423000 34200 2610 180 10
6
5040000 423000 34200 2610 180 10
7
58500000 5040000 423000 34200 2610 180 10
8
666000000 58500000 5040000 423000 34200 2610 180 10
有了上面的数据,发现规律已经很明显了,都不用上排列组合,已经可以直接上手代码了,一开始想编O(n)代码,编不出,那就先编能满足上述数据规律的代码吧,编好后一看,是O(n^2)的代码,代码如下
#include <stdio.h>
#define maxn 200010
#define LL long long
#define mod 998244353
LL a[maxn],pow[maxn],sum[maxn];
int main(){
int n,i,j;
scanf("%d",&n);
pow[0]=1;
for(i=1;i<=n;i++)pow[i]=pow[i-1]*10%mod;
a[1]=10;
for(i=2;i<=n;i++){
a[i]=i*pow[i]%mod;
for(j=1;j<i;j++)
a[i]=((a[i]-a[j]*(i-j+1)%mod)%mod+mod)%mod;
}
for(i=n;i>=2;i--)printf("%lld ",a[i]);
printf("%lld\n",a[1]);
return 0;
}
若看不懂代码,请看数据模拟如下
1
10
2
180 10
00,01,02,......,97,98,99有100个数据,每个数据包含2个数字,总的数字个数是2*100
00,11,22,33,44,55,66,77,88,99有10个数据,每个数据包含2个数字,总的数字个数是10*2
容斥原理180=2*100-10*2
3
2610 180 10
2610=3*1000-180*2-10*3
4
34200 2610 180 10
34200=4*10000-2610*2-180*3-10*4
对公式进行优化
for(j=1;j<i;j++)
a[i]=((a[i]-a[j]*(i-j+1)%mod)%mod+mod)%mod;
可简化为
for(j=1;j<i;j++)
a[i]-a[j]*(i-j+1)
计算其中的部分公式
for(j=1;j<i;j++)
a[j]*(i-j+1)
以i=4为例
for(j=1;j<4;j++)
a[j]*(4-j+1)
可以拆成
for(j=1;j<4;j++)
a[j]*4-a[j]*(j-1)
a[j]*4
a[1]*4+a[2]*4+a[3]*4=(a[1]+a[2]+a[3])*4=sum[3]*4 令sum[i]=a[1]+a[2]+...+a[i]
a[j]*(j-1)
a[1]*0+a[2]*1+a[3]*2=A[3]
以下为AC代码
#include <stdio.h>
#define maxn 200010
#define LL long long
#define mod 998244353
LL a[maxn],pow[maxn],sum[maxn],A[maxn];
int main(){
int n,i,j;
scanf("%d",&n);
pow[0]=1;
for(i=1;i<=n;i++)pow[i]=pow[i-1]*10%mod;
a[1]=10,sum[1]=10,A[1]=0;
for(i=2;i<=n;i++){
a[i]=i*pow[i]%mod;
a[i]=((a[i]-sum[i-1]*i+A[i-1])%mod+mod)%mod;
sum[i]=(sum[i-1]+a[i])%mod,A[i]=(A[i-1]+a[i]*(i-1)%mod)%mod;
}
for(i=n;i>=2;i--)printf("%lld ",a[i]);
printf("%lld\n",a[1]);
return 0;
}