zoukankan      html  css  js  c++  java
  • CF840C On the Bench 和 LG6151 青春猪头少年不会梦到兔女郎学姐

    On the Bench

    (N)种球,第(i)种球有(A_i)个,求排列方式,使得相同种类的球不相邻。

    每种球内部是否区分只是答案后面是否有个系数的差别,不用纠结。

    (sum_i A_i ≤ 3000)

    老做法

    运用插空法最好可以做到 (O(n^3))。但是这个做法已经过时了。

    Atcoder Typical DP Contest:O用的就是这个做法。

    题解

    限制是相邻两个球不能相同。当违反限制的时候,可以看成相邻两个球粘在了一起。

    对于第(i)种球,如果违反了(j(0 ≤ j < A_i))个限制,就相当于有(j)对相邻的球被粘在了一起,方案数是 (inom{A_i−1}{j}) ,带上容斥系数就是((−1)^j),这时候可以看成有(A_i − j)个球拿出去任意排列。

    用背包DP把对每种球的容斥过程合并到一起即可。时间复杂度是(O((sum_i A_i)^2))的。

    也可以用多项式优化做到更好的复杂度。

    CO int N=300+10;
    int fac[N],ifac[N];
    int val[N],cnt[N];
    int dp[N][N];
    
    IN int C(int n,int m){
    	return mul(fac[n],mul(ifac[m],ifac[n-m]));
    }
    int main(){
    	int n=read<int>();
    	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);
    	for(int i=1;i<=n;++i){
    		read(val[i]);
    		bool flag=0;
    		for(int j=1;j<i;++j){
    			int64 s=(int64)val[i]*val[j],t=sqrt(s);
    			if(t*t==s){
    				++cnt[j];
    				flag=1;break;
    			}
    		}
    		if(!flag) ++cnt[i];
    	}
    	int m=0;
    	for(int i=1;i<=n;++i)if(cnt[i]) cnt[++m]=cnt[i];
    	dp[0][0]=1;
    	int s=0;
    	for(int i=1;i<=m;++i){
    		for(int j=0;j<=s;++j)if(dp[i-1][j])
    			for(int k=1;k<=cnt[i];++k){
    				if((cnt[i]-k)%2==0)
    					dp[i][j+k]=add(dp[i][j+k],mul(dp[i-1][j],mul(C(cnt[i]-1,k-1),C(j+k,k))));
    				else
    					dp[i][j+k]=add(dp[i][j+k],mod-mul(dp[i-1][j],mul(C(cnt[i]-1,k-1),C(j+k,k))));
    			}
    				
    		s+=cnt[i];
    	}
    	int ans=0;
    	for(int j=0;j<=s;++j) ans=add(ans,dp[m][j]);
    	for(int i=1;i<=m;++i) ans=mul(ans,fac[cnt[i]]);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    青春猪头少年不会梦到兔女郎学姐

    (N)种球,第(i)种球有(A_i)个。

    对于一个序列,把它看成首尾相连的。一个序列的权值定义为每个极大相同颜色连续段长度的乘积。

    求所有序列的权值和,对(998244353)取模。

    (sum_i A_i ≤ 2 × 10^5)

    题解

    仓鼠《杂题选讲》。

    想象这么一种暴力。假如枚举每种颜色最后分段是什么样的,那么可以直接用前面说的容斥做法统计方案数,乘上这种分段带来的价值加到答案里面去。

    实际上可以把每种暴力的结果合并到一起,丢到后面的容斥的过程里面去。

    先优化暴力的过程,数量为(A_i)的物品分成(n)段的贡献和用之前说的插板法计算。相当于每一段要选一个代表点。

    然后用FFT计算出,每个分成(n)段的情况在容斥的时候又被分成了(m)段的方案数。

    直接用分治FFT把容斥的情况合并到一起。

    注意需要处理一个首尾成环的情况,可以考虑加一维表示目前有没有确定开头的颜色。

    对于开头的颜色,需要特殊处理贡献。相当于第一段独立重复选两次代表点。

    时间复杂度 (O(nlog^2 n))

    CO int N=262144;
    int omg[2][N],rev[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==1){
    		int ilim=fpow(lim,mod-2);
    		for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim);
    	}
    }
    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 operator+(poly a,poly b){
    	int n=max(a.size(),b.size());
    	a.resize(n),b.resize(n);
    	for(int i=0;i<n;++i) a[i]=add(a[i],b[i]);
    	return a;
    }
    
    int fac[N],ifac[N];
    poly f[N],g[N];
    
    IN int C(int n,int m){
    	if(m<0 or m>n) return 0;
    	return mul(fac[n],mul(ifac[m],ifac[n-m]));
    }
    pair<poly,poly> solve(int l,int r){
    	if(l==r) return {f[l],g[l]};
    	int mid=(l+r)>>1;
    	pair<poly,poly> left=solve(l,mid),right=solve(mid+1,r);
    	return {left.first*right.first,left.first*right.second+left.second*right.first};
    }
    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);
    	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[0]=1;
    	for(int i=1;i<N;++i) fac[i]=mul(fac[i-1],i);
    	ifac[N-1]=fpow(fac[N-1],mod-2);
    	for(int i=N-2;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
    	
    	int n=read<int>();
    	if(n==1){
    		printf("%d
    ",read<int>());
    		return 0;
    	}
    	
    	for(int i=1;i<=n;++i){
    		int c=read<int>();
    		f[i].resize(c+1),g[i].resize(c+1);
    		for(int j=1;j<=c;++j){
    			f[i][j]=C(c+j-1,2*j-1);
    			g[i][j]=add(f[i][j],mul(2,C(c+j-1,2*j)));
    		}
    //		cerr<<"f=";
    //		for(int j=1;j<=c;++j) cerr<<" "<<f[i][j];
    //		cerr<<endl;
    //		cerr<<"g=";
    //		for(int j=1;j<=c;++j) cerr<<" "<<g[i][j];
    //		cerr<<endl;
    		
    		poly h(c+1);
    		for(int j=1;j<=c;++j) f[i][j]=mul(f[i][j],fac[j-1]);
    		for(int j=0;j<=c;++j) h[j]=j%2==1?mod-ifac[j]:ifac[j];
    		reverse(h.begin(),h.end());
    		f[i]=f[i]*h;
    		for(int j=0;j<=c;++j) f[i][j]=f[i][j+c];
    		f[i].resize(c+1);
    		f[i][0]=0;
    		for(int j=1;j<=c;++j) f[i][j]=mul(f[i][j],ifac[j-1]);
    		
    		for(int j=1;j<=c;++j) g[i][j]=mul(g[i][j],fac[j]);
    		for(int j=0;j<=c;++j) h[j]=j%2==1?mod-ifac[j]:ifac[j];
    		reverse(h.begin(),h.end());
    		g[i]=g[i]*h;
    		for(int j=0;j<=c;++j) g[i][j]=g[i][j+c];
    		g[i].resize(c+1);
    		g[i][0]=0;
    		for(int j=1;j<=c;++j) g[i][j]=mul(g[i][j],ifac[j]);
    		
    //		cerr<<"nf=";
    //		for(int j=1;j<=c;++j) cerr<<" "<<f[i][j];
    //		cerr<<endl;
    //		cerr<<"ng=";
    //		for(int j=1;j<=c;++j) cerr<<" "<<g[i][j];
    //		cerr<<endl;
    		
    		for(int j=1;j<=c;++j){
    			f[i][j]=mul(f[i][j],ifac[j]);
    			g[i][j]=mul(g[i][j],ifac[j-1]);
    		}
    	}
    	
    	poly res=solve(1,n).second;
    	int ans=0;
    	for(int i=n;i<(int)res.size();++i)
    		ans=add(ans,mul(res[i],fac[i-1]));
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    201871010113-刘兴瑞 实验二 个人项目—《背包问题》项目报告
    201871010113-刘兴瑞 实验一 软件工程准备—初识软件工程
    201871010113-刘兴瑞《面向对象程序设计(java)》课程学习总结
    201871010113-刘兴瑞《面向对象程序设计(java)》第十七周学习总结
    201871010113-刘兴瑞《面向对象程序设计(java)》第十六周学习总结
    201871010113-刘兴瑞《面向对象程序设计(java)》第十五周学习总结
    201871010113-刘兴瑞《面向对象程序设计(java)》第十四周学习总结
    201871010113-刘兴瑞《面向对象程序设计(java)》第十三周学习总结
    《2019面向对象程序设计(java)课程学习进度条》
    201871010114-李岩松 实验四 团队作业1:软件研发团队组建(个人博客)
  • 原文地址:https://www.cnblogs.com/autoint/p/12556951.html
Copyright © 2011-2022 走看看