csp模擬 字符串問題【計數】【組合數學】

傳送門

又是一個計數題,,

在n個數中間填加號,求所有方案的數字和。

下面提供兩種解法,題解的和來自FSY的。

題解:

考慮每個區間對數的貢獻,要麼沒有貢獻,要麼貢獻是10的冪。

故我們可以枚舉使得這個數的係數爲10^i時,區間的個數。發現是個組合數。

發現這樣枚舉實際上是固定了該點所在區間的右端點,也就是說固定了一個加號的位置。設當前點爲i,對i的貢獻爲10^j,則右端點爲i+j。

發現i~i+j不能填加號,共j個位置。而i+j右邊強制加了一個加號。所以剩下的加號方案爲\binom{n-j-2}{m-1}

故貢獻爲\sum_{i=1}^n\sum_{j=0}^{n-i}10^j\binom{n-j-2}{m-1}a_i

發現複雜度n^2,考慮交換枚舉順序。如果先枚舉j,即先枚舉區間右端點到點i的距離。

\sum_{j=0}^n10^j\binom{n-j-2}{m-1}\sum_{i=1}^{n-j}a_i

於是等差數列求和即可。

 

方法2:

考慮枚舉區間,計算區間被計算的次數。

在不考慮1和n兩個端點的情況下,考慮一個區間[l,r]。

使得這個區間存在的條件是區間左右各有一個加號,區間內部沒有加號。

所以加號的組合方案爲\binom{n-len-2}{m-2}。故貢獻爲\sum_{i=1}^n\sum_{j=i}^nsum[i\to j]\binom{n-len-2}{m-2}

發現複雜度爲n^2,發現組合數只跟區間長度len有關,考慮如何計算長度爲len的所有區間和。

舉例子時間:

如果有樣例abcdefg

發現長度爲1的和是a,b,c,d,e,f,g

長度爲2的是ab,bc,cd,de,ef,fg

也就是10*(a+b+c+d+e+f)+(b+c+d+e+f+g)。

多舉幾個例子,你會發現,對於一個長度len,其答案爲(len-1)的答案去掉最後一項*10,然後加上一個個位的後綴和。

所以維護後綴和然後該怎麼計算怎麼計算。

對於區間端點爲1或者n的,自己手玩一下該怎麼算,區別不大。

#include<bits/stdc++.h>
using namespace std;
#define in read()
#define int long long
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;ch=getchar();
	}return cnt*f;
}
int fac[1000003],ifac[1000003];
int sum[1000003];
int suml[1000003];
int n,m;char ch[1000003];
const int mod=998244353;
int ksm(int a,int b){
	int gu=1;
	while(b){
		if(b&1)gu=gu*a%mod;a=a*a%mod;b>>=1;
	}return gu;
}
int jz[1000003];
int sumsuf[1000003];
int tem;
signed main(){
	n=in;m=in;fac[0]=ifac[0]=1;jz[0]=1;
	scanf("%s",ch+1);for(int i=1;i<=n;i++)jz[i]=jz[i-1]*10%mod;
	for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;ifac[n]=ksm(fac[n],mod-2);
	for(int i=n-1;i>=1;i--)ifac[i]=ifac[i+1]*(i+1)%mod;
	for(int i=1;i<=n;i++)sum[i]=(sum[i-1]*10+ch[i]-'0')%mod;
	for(int i=n-1;i>=1;i--)sumsuf[i]=(sumsuf[i+1]+ch[i]-'0')%mod;
	for(int i=2;i<=n-1;i++)suml[1]+=ch[i]-'0',suml[1]%=mod;
	tem=suml[1]-ch[n-1]+'0';
	for(int i=2;i<n;i++){
		suml[i]=10*tem%mod+sumsuf[i+1];suml[i]%=mod;
		tem=suml[i]-(sum[n-1]-sum[n-i-1]*jz[i]%mod)%mod;tem=(tem%mod+mod)%mod;
	}`
//	for(int i=1;i<=n;i++){
//		cout<<suml[i]<<" ";
//	}cout<<endl;
	int ans=0;
	for(int i=1;i<=n;i++){
		int x=m-2,y=n-i-2;
		if(x>y||x<0||y<0)continue;
//		cout<<x<<" "<<y<<" "<<suml[i]<<" "<<fac[y]<<" "<<ifac[x]<<" "<<ifac[y-x]<<endl;
		ans=(ans+suml[i]*fac[y]%mod*ifac[x]%mod*ifac[y-x]%mod)%mod;
		//c/out<<ans<<endl;
	}//cout<<ans<<endl;
	for(int i=1;i<n;i++){
		int x=m-1,y=n-1-i;if(x<0||y<0| |y<x)continue;
		ans=(ans+sum[i]*fac[y]%mod*ifac[x]%mod*ifac[y-x]%mod)%mod;
	}
	//cout<<ans<<endl;
	for(int i=2;i<=n;i++){
		int x=m-1,y=i-2;if(x<0||y<0||y<x)continue;
//		cout<<i<<" "<<sum[n]<<" "<<sum[i-1]<<" "<<jz[n-i+1]<<" "<<sum[n]-sum[i-1]*jz[n-i+1]<<endl;
		ans=(ans+(sum[n]-sum[i-1]*jz[n-i+1]%mod+mod)%mod*fac[y]%mod*ifac[x]%mod*ifac[y-x]%mod)%mod;
	}
//	ans=(ans+sum[n]*(m==0)+mod)%mod;
	cout<<ans;
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章