PE427 n-sequences 和 ZJOI2020 抽卡

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;
}

抽卡

Bob 喜欢抽卡。

Bob 最近入坑了一款叫“主公连结” 的抽卡游戏。游戏中共有 \(n\) 个不同的角色,编号为 \(1\sim n\)。当 Bob 获得其中的编号连续的 \(k\) 张卡时,就可以组出理论最强阵容。

当期卡池中共有 \(m\) 张不同的卡,每次抽卡,Bob 都可以等概率随机获得一张卡池中的卡。如果 Bob 抽到了一张他已经拥有的卡,那么什么事都不会发生,等于 Bob 浪费了这次抽卡机会。Bob 是个谨慎的人,他想知道,如果他不停地抽卡直到抽到编号连续的 \(k\) 张卡时停止抽卡,期望需要抽多少轮。

对于 \(100\%\) 的数据,\(1 \le m \le 200000, 1 \le a_i \le 2m, 2 \le k \le m\),保证卡池中至少存在一组可抽出的理论最强阵容(即编号连续的 \(k\) 张卡)。

题解

期望步数=期望经过的不合法的状态数=每个不合法的状态被经过的概率×期望停留次数之和。

\[\text{ans}=\sum_{S}\frac{1}{\binom{m}{|S|}}\frac{1}{1-\frac{|S|}{m}} \]

对于那些已经合法的状态,我们可以认为它们会继续抽卡。这样做对不合法的状态被走到的概率没有影响,所以经过大小为\(|S|\)的状态概率是\(\frac{1}{\binom{m}{|S|}}\)

在不合法的状态上期望停留的步数显然是个几何分布的期望,即\(\frac{1}{1-\frac{|S|}{m}}\)

那么我们现在问题的关键在于,对于每个\(i\),求出大小为\(i\)的不合法的集合的个数。

卡池里的卡可以被分为不同的连续段,而不合法的状态的要求就是每个连续段内不能选出\(k\)个连续的数。注意到连续段之间的选择独立,所以可以对每个连续段分别DP,最后通过某种手段(多半是卷积)合并到一起。

\(f(i,j)\)表示前\(i\)个数选了\(j\)个,且没有连续\(k\)个数的方案数,有\(f(0,0)=1\)

  1. \(0\leq i\leq k-1\)时,\(f(i,j)=f(i-1,j)+f(i-1,j-1)\)

  2. \(i=k\)时,减去前\(k\)个数都被选了的方案,\(f(i,j)=f(i-1,j)+f(i-1,j-1)-f(i-k,j-k)\)

  3. \(k+1\leq i\leq n\)时,容斥掉\(i-k+1\sim i\)都被选了的方案,此时\(i-k\)一定没有选,\(f(i,j)=f(i-1,j)+f(i-1,j-1)-f(i-k-1,j-k)\)

假设没有第2种转移,写成生成函数

\[F_i(x)=(1+x)F_{i-1}(x)-x^kF_{i-k-1}(x) \]

又成了走楼梯问题,考虑求出\(F_n(x)\)

\[F_n(x)=\sum_{i=0}^{\lfloor\frac{n}{k+1}\rfloor}(1+x)^{n-i(k+1)}(-x^k)^{i}\binom{n-i(k+1)+i}{i} \]

\(n\bmod (k+1)\)的那部分\((1+x)\)提出去,设\(m=\lfloor\frac{n}{k+1}\rfloor\)

\[A(x)=-x^k,B(x)=(1+x)^{k+1},C_i=\binom{n-i(k+1)+i}{i} \]

\[F_n(x)=(1+x)^{n\bmod (k+1)}\sum_{i=0}^mA(x)^iB(x)^{m-i}C_i \]

考虑分治FFT求\(\sum_{i=0}^mA(x)^iB(x)^{m-i}C_i\)。设

\[G_{l,r}(x)=\sum_{i=l}^rA(x)^{i-l}B(x)^{r-i}C_i \]

\[G_{l,r}(x)=G_{l,\text{mid}}(x)B(x)^{r-\text{mid}}+G_{\text{mid}+1,r}(x)A(x)^{\text{mid+1}-l} \]

加上第2种转移,也无非是多了强制第一次转移走\(k\)步贡献\(-x^k\)

时间复杂度\(T(m)=2T(m/2)+O(mk\log(mk))=O(n\log^2n)\)

CO int N=1<<18;
int omg[2][N],rev[N];
int fac[N],inv[N],ifac[N];

void NTT(poly&a,int dir){
	int lim=a.size(),len=log2(lim);
	for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
	for(int i=0;i<lim;++i)if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=1;i<lim;i<<=1)
		for(int j=0;j<lim;j+=i<<1)for(int k=0;k<i;++k){
			int t=mul(omg[dir][N/(i<<1)*k],a[j+i+k]);
			a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
		}
	if(dir){
		int ilim=fpow(lim,mod-2);
		for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim);
	}
}
poly operator+(poly a,CO poly&b){
	if(a.size()<b.size()) a.resize(b.size());
	for(int i=0;i<(int)b.size();++i) a[i]=add(a[i],b[i]);
	return a;
}
poly operator*(poly a,poly b){
	int n=a.size()+b.size()-1,lim=1<<(int)ceil(log2(n));
	a.resize(lim),NTT(a,0);
	b.resize(lim),NTT(b,0);
	for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
	NTT(a,1),a.resize(n);
	return a;
}
poly pow(poly a,int b){
	int n=a.size()-1,lim=1<<(int)ceil(log2(n*b+1));
	a.resize(lim),NTT(a,0);
	for(int i=0;i<lim;++i) a[i]=fpow(a[i],b);
	NTT(a,1),a.resize(n*b+1);
	return a;
}

IN int C(int n,int m){
	return n<m?0:mul(fac[n],mul(ifac[m],ifac[n-m]));
}

int c[N];
poly a,b;

poly solve(int l,int r){
	if(l==r) return poly{c[l]};
	int mid=(l+r)>>1;
	return solve(l,mid)*pow(b,r-mid)+solve(mid+1,r)*pow(a,mid+1-l);
}
poly real_main(int n,int k){
	a.assign(k+1,0),a[k]=mod-1;
	b=pow(poly{1,1},k+1);
	for(int i=0;i*(k+1)<=n;++i) c[i]=C(n-i*(k+1)+i,i);
	poly f=solve(0,n/(k+1))*pow(poly{1,1},n%(k+1));
	if(n<k) return f;
	for(int i=0;i*(k+1)<=n-k;++i) c[i]=C(n-k-i*(k+1)+i,i);
	f=f+solve(0,(n-k)/(k+1))*pow(poly{1,1},(n-k)%(k+1))*a;
	return f;
}
int main(){
	omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N);
	omg[1][0]=1,omg[1][1]=fpow(omg[0][1],mod-2);
	fac[0]=fac[1]=1;
	inv[0]=inv[1]=1;
	ifac[0]=ifac[1]=1;
	for(int i=2;i<N;++i){
		omg[0][i]=mul(omg[0][i-1],omg[0][1]);
		omg[1][i]=mul(omg[1][i-1],omg[1][1]);
		fac[i]=mul(fac[i-1],i);
		inv[i]=mul(mod-mod/i,inv[mod%i]);
		ifac[i]=mul(ifac[i-1],inv[i]);
	}
	static int a[N];
	int m=read<int>(),k=read<int>();
	for(int i=1;i<=m;++i) read(a[i]);
	sort(a+1,a+m+1);
	poly f={1};
	for(int l=1,r;l<=m;l=r+1){
		for(r=l;r+1<=m and a[r+1]==a[r]+1;++r);
		f=f*real_main(r-l+1,k);
	}
	f.resize(m+1);
//	cerr<<"f=";
//	for(int x:f) cerr<<" "<<x;
//	cerr<<endl;
	int ans=0;
	for(int i=0;i<m;++i)
		ans=add(ans,mul(f[i],mul(fpow(C(m,i),mod-2),mul(m,inv[m-i]))));
	printf("%d\n",ans);
	return 0;
}

posted on 2020-07-21 19:05  autoint  阅读(188)  评论(0编辑  收藏

导航