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