[codeforces 1327E] Count The Blocks 打表找规律+根据规律找公式+优化公式

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;
}
发布了631 篇原创文章 · 获赞 553 · 访问量 47万+

猜你喜欢

转载自blog.csdn.net/mrcrack/article/details/105075574