1. 程式人生 > 實用技巧 >PE427 n-sequences

PE427 n-sequences

n-sequences

對於⼀個序列\(S\),令\(L(S)\)表示\(S\)中最長的值相同的子串的⻓度。

\(f(n)\)表示對於所有\(n^n\)個長度為\(n\)的每個數值都在\(1\)\(n\)之間序列的\(L\)值總和。

\(f(7.5e6)\)

題解

⾸先轉化為求\(L(S)\geq 1, L(S)\geq 2, …\)的方案然後相加。

接著補集轉化為\(L(S)\leq k\)的方案,也就是每段都不超過\(k\)的方案。

\(g(i)\)表示滿足條件的長度為\(i\)的序列的種數,有\(g(0)=1\)

  1. \(1\leq i\leq k\)時,\(g(i)=n\times g(i-1)\)

  2. \(i=k+1\)時,恰好有\(n\)種方案不合法,\(g(i)=n\times g(i-1)-n\times g(0)\)

  3. \(k+2\leq i\leq n\)時,容斥掉\(i-k\sim i\)都是同色的的方案。那麼由於\(g(i-1)\)的限制,\(i-k-1\)\(i-k\)的顏色必須不同。所以\(g(i)=n\times g(i-1)-(n-1)\times g(i-k-1)\)

直接計算的話時間複雜度\(O(n^2)\)不可取。

CO int N=100;
int64 g[N];

int main(){
	int n=11;
	int64 pwr=pow(n,n),ans=0;
	for(int k=0;k<n;++k){
		g[0]=1;
		for(int i=1;i<=k;++i) g[i]=n*g[i-1];
		g[k+1]=n*g[k]-n*g[0];
		for(int i=k+2;i<=n;++i) g[i]=n*g[i-1]-(n-1)*g[i-k-1];
		ans+=pwr-g[n];
	}
	printf("%lld\n",ans);
	return 0;
}

假設沒有第2種轉移,考慮這個遞推式的組合意義。相當於走樓梯,往上走\(1\)步的代價是乘\(n\),走\(k+1\)步的代價是乘\(-(n-1)\)

那麼我們列舉一共做了多少次走\(k+1\)

\[\sum_{i(k+1)\leq n}n^{n-i(k+1)}(1-n)^{i}\binom{n-i(k+1)+i}{i} \]

第二種轉移無非是列舉第一次的決策拆開來計算

\[\sum_{i(k+1)\leq n}(n^{n-i(k+1)}(1-n)^{i}\binom{n-i(k+1)-1+i}{i}-n^{n-i(k+1)+1}(1-n)^{i-1}\binom{n-i(k+1)+i-1}{i-1}) \]

時間複雜度調和級數\(O(n\ln n)\)

CO int N=1e7;
int fac[N],ifac[N];
int pn[N],p1n[N];

IN int C(int n,int m){
	if(n<m) return 0;
	return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
int main(){
	int n=7.5e6;
	fac[0]=1;
	for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
	ifac[n]=fpow(fac[n],mod-2);
	for(int i=n-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
	pn[0]=p1n[0]=1;
	for(int i=1;i<=n;++i){
		pn[i]=mul(pn[i-1],n);
		p1n[i]=mul(p1n[i-1],1+mod-n);
	}
	int ans=0;
	for(int k=0;k<n;++k){
		if(k==0){
			ans=add(ans,pn[n]);
			continue;
		}
		int sum=0;
		for(int i=0;i*(k+1)<=n;++i){
			if(i==0){
				sum=add(sum,pn[n]);
				continue;
			}
			sum=add(sum,mul(pn[n-i*(k+1)],mul(p1n[i],C(n-i*(k+1)-1+i,i))));
			sum=add(sum,mod-mul(pn[n-i*(k+1)+1],mul(p1n[i-1],C(n-i*(k+1)+i-1,i-1))));
		}
		ans=add(ans,add(pn[n],mod-sum));
	}
	printf("%d\n",ans);
	return 0;
}