zoukankan      html  css  js  c++  java
  • 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. (1leq ileq k)时,(g(i)=n imes g(i-1))

    2. (i=k+1)时,恰好有(n)种方案不合法,(g(i)=n imes g(i-1)-n imes g(0))

    3. (k+2leq ileq n)时,容斥掉(i-ksim i)都是同色的的方案。那么由于(g(i-1))的限制,(i-k-1)(i-k)的颜色必须不同。所以(g(i)=n imes g(i-1)-(n-1) imes 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
    ",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}inom{n-i(k+1)+i}{i} ]

    第二种转移无非是枚举第一次的决策拆开来计算

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

    时间复杂度调和级数(O(nln 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
    ",ans);
    	return 0;
    }
    

    抽卡

    Bob 喜欢抽卡。

    Bob 最近入坑了一款叫“主公连结” 的抽卡游戏。游戏中共有 (n) 个不同的角色,编号为 (1sim 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) 张卡)。

    题解

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

    [ ext{ans}=sum_{S}frac{1}{inom{m}{|S|}}frac{1}{1-frac{|S|}{m}} ]

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

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

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

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

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

    1. (0leq ileq 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+1leq ileq n)时,容斥掉(i-k+1sim 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}^{lfloorfrac{n}{k+1} floor}(1+x)^{n-i(k+1)}(-x^k)^{i}inom{n-i(k+1)+i}{i} ]

    (nmod (k+1))的那部分((1+x))提出去,设(m=lfloorfrac{n}{k+1} floor)

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

    [F_n(x)=(1+x)^{nmod (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, ext{mid}}(x)B(x)^{r- ext{mid}}+G_{ ext{mid}+1,r}(x)A(x)^{ ext{mid+1}-l} ]

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

    时间复杂度(T(m)=2T(m/2)+O(mklog(mk))=O(nlog^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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    点云数据的存储格式
    模块编写流程
    特征描述子
    指针和引用的差别
    内联函数和宏定义的差别
    哪些函数不能为virtual函数
    如何定义一个只能在堆上(栈上)生成对象的类
    对象深拷贝问题
    Warning: Failed prop type: Invalid prop `value` supplied to `Picker`.报错问题
    解决多层数组、对象深拷贝问题
  • 原文地址:https://www.cnblogs.com/autoint/p/13356885.html
Copyright © 2011-2022 走看看