集训队作业2018: 青春猪头少年不会梦到兔女郎学姐(多限制容斥)

前言:
虽然这道题的名字有点那啥,但是题还是很好的,听说是某道原题的加强版。

题意:
给定 n n n种颜色的球,第 i i i种颜色的球数量为 A i A_i Ai个,保证 ∑ i = 1 n A i ≤ 2 ∗ 1 0 5 \sum_{i=1}^n A_i \le 2*10^5 i=1nAi2105,对于这所有 ( ∑ A i ) ! ∏ A i ! \frac{(\sum_{A_i})!}{\prod A_i!} Ai!(Ai)!的排列,一种排列的贡献可以如下计算:先把这个序列首尾相连,然后把所有相邻且颜色相同的段拿出来,贡献为他们的长度之积,求所有贡献和。

题解:
这道题一开始就想偏到枚举循环节去了,看了题解才知道可以用容斥来算。

首先考虑算贡献时首尾不相连怎么做,这个时候我们可以枚举 a i a_i ai被分为了 b i b_i bi段,设其代价和为 f ( a i , b i ) f(a_i,b_i) f(ai,bi),那么贡献为 ∏ f ( a i , b i ) ∗ ways \prod f(a_i,b_i)*\text{ways} f(ai,bi)ways ways \text{ways} ways表示所有 b i b_i bi个段拼起来相邻不同的方案数。

这个其实可以用多个颜色来一起容斥,我们再枚举一下最后每种颜色有几个断点被合并了,可以得到:
ways = ∑ c ( ∏ ( b i − 1 c i ) ( − 1 ) c i ) ( ∑ b i − c i ) ! ∏ ( ( b i − c i ) ! ) \text{ways} = \sum_c (\prod\binom{b_i-1}{c_i}(-1)^{c_i}) \frac{(\sum b_i-c_i)!}{\prod((b_i-c_i)!)} ways=c((cibi1)(1)ci)((bici)!)(bici)!

然后其实就是要算这个玩意儿:
∑ b ∑ c ( ∏ f a i , b i ( b i − 1 c i − 1 ) ( − 1 ) b i − c i ) ( ∑ c i ) ! ∏ c i ! \sum_b\sum_c (\prod f_{a_i,b_i} \binom{b_i-1}{c_i-1}(-1)^{b_i-c_i})\frac{(\sum c_i)!}{\prod c_i!} bc(fai,bi(ci1bi1)(1)bici)ci!(ci)!

我们可以考虑把每个颜色球关于 c c c的egf搞出来,相当于是对每个 c c c,求:
∑ b ≥ c ( b − 1 c − 1 ) ( − 1 ) b − c f a , b \sum_{b\ge c} \binom{b-1}{c-1}(-1)^{b-c}f_{a,b} bc(c1b1)(1)bcfa,b

然后分治FFT即可得到求答案的egf。

考虑一下 c c c的egf怎么求,观察一下这个 f ( a , b ) f(a,b) f(a,b),不难发现其等于 ( a + b − 1 2 ∗ b − 1 ) = ( a + b − 1 a − b ) \binom{a+b-1}{2*b-1} = \binom{a+b-1}{a-b} (2b1a+b1)=(aba+b1),相当于先分成 b b b段,插 b − 1 b-1 b1个板,然后每段选 1 1 1个数,这样两个组合数卷积也可以FFT优化一下。

然后考虑一个环怎么做,我们强制规定这个序列从1开头,不以1结尾就行了, c 1 c_1 c1的egf那个 c c c要变成 c − 1 c-1 c1,可以算出从1开头的方案数, c 1 c_1 c1的egf的 c c c变成 c − 2 c-2 c2可以算出以1开头且以1结尾的方案数,减一下就可以得到以1开头不以1结尾的方案数。

不过注意一下,这里如果每 T T T个就出现循环的话,最后会计算 b m T \frac{b}{\frac{m}{T}} Tmb次,而我们要求计算 T T T次,我们最后乘的时候注意对 c 1 c_1 c1的那个egf算组合数卷积的时候先除个 b b b,最后乘上 m m m就会发现刚好计算了 T T T次。

时间复杂度 O ( m log ⁡ 2 m ) O(m \log^2 m) O(mlog2m)

#include <bits/stdc++.h>
using namespace std;

const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}

const int N=2e6+50, mod=998244353, G=3;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);}
inline int mul(int x,int y) {return (long long)x*y%mod;}
inline int power(int a,int b,int rs=1) {for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a); return rs;}
inline int sgn(int x) {return (x&1) ? mod-1 : 1;}

namespace FFT {
	int A[N],B[N],pos[N],w[N],k;
	inline void init(int n) {
		for(k=1;k<=n;k<<=1);
		memset(A,0,sizeof(int)*k);
		memset(B,0,sizeof(int)*k);
		for(int i=1;i<k;i++) pos[i]=(i&1) ? ((pos[i>>1]>>1)^(k>>1)) : (pos[i>>1]>>1);
	}
	inline void dft(int *a) {
		for(int i=1;i<k;i++)
			if(pos[i]>i) swap(a[pos[i]],a[i]);
		for(int bl=1;bl<k;bl<<=1) {
			int tl=bl<<1, wn=power(G,(mod-1)/tl);
			w[0]=1; for(int i=1;i<bl;i++) w[i]=mul(w[i-1],wn);
			for(int bg=0;bg<k;bg+=tl)
				for(int j=0;j<bl;j++) {
					int &t1=a[bg+j], &t2=a[bg+j+bl], t=mul(t2,w[j]);
					t2=dec(t1,t); t1=add(t1,t);
				}
		}
	}
	inline void poly_mul() {
		dft(A); dft(B);
		for(int i=0;i<k;i++) B[i]=mul(A[i],B[i]);
		dft(B); reverse(B+1,B+k);
		const int inv=power(k,mod-2);
		for(int i=0;i<k;i++) B[i]=mul(B[i],inv);
	}
}

struct combin {
	int fac[N],ifac[N];
	combin() {
		fac[0]=1;
		for(int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
		ifac[0]=ifac[1]=1;
		for(int i=2;i<N;i++) ifac[i]=mul(mod-mod/i,ifac[mod%i]);
		for(int i=2;i<N;i++) ifac[i]=mul(ifac[i-1],ifac[i]);
	}
	inline int C(int a,int b) {return mul(fac[a],mul(ifac[b],ifac[a-b]));}
	inline int inv(int x) {return x ? mul(ifac[x],fac[x-1]) : 1;}
} C;
struct poly {
	vector <int> a;
	poly(int d=0,int t=0) {a.resize(d+1); a[d]=t;}
	inline int deg() const {return a.size()-1;}
	inline int& operator[](int i) {return a[i];}
	inline const int& operator[](int i) const {return a[i];}
	friend inline poly operator *(const poly &a,const poly &b) {
		poly c(a.deg()+b.deg(),0); FFT::init(c.deg());
		for(int i=0;i<=a.deg();i++) FFT::A[i]=a[i];
		for(int i=0;i<=b.deg();i++) FFT::B[i]=b[i];
		FFT::poly_mul();
		for(int i=0;i<=c.deg();i++) c[i]=FFT::B[i];
		return c;
	}
	inline void pt() {
		for(int i=0;i<=deg();i++) cerr<<a[i]<<' '; cerr<<'\n';
	}
} f[N/10];

int n,m,a[N],ans;
inline poly solve(int l,int r) {
	if(l==r) {
		for(int i=1;i<=f[l].deg();i++)
			f[l][i]=mul(f[l][i],C.ifac[i]);
		return f[l];
	} int mid=(l+r)>>1;
	return solve(l,mid)*solve(mid+1,r);
}
int main() {
	n=rd();
	if(n==1) {printf("%d\n",rd()); return 0;}
	for(int i=1;i<=n;i++) a[i]=rd(), m+=a[i];
	for(int i=1;i<=n;i++) {
		poly g(a[i],0),h(a[i],0);
		for(int j=1;j<=a[i];j++) g[j]=mul(C.fac[j-1],C.C(a[i]+j-1,a[i]-j));
		if(i==1) for(int j=1;j<=a[i];j++) g[j]=mul(g[j],C.inv(j));
		for(int j=0;j<=a[i];j++) h[a[i]-j]=mul(C.ifac[j],sgn(j));
		g=g*h; f[i]=poly(a[i],0);
		for(int j=1;j<=a[i];j++) f[i][j]=mul(g[a[i]+j],C.ifac[j-1]);
	}
	poly g=solve(2,n);
	poly h(a[1]-1,0);
	for(int i=0;i<=h.deg();i++)
		h[i]=mul(f[1][i+1],C.ifac[i]);
	h=h*g;
	for(int i=1;i<=h.deg();i++) 
		ans=add(ans,mul(h[i],C.fac[i]));
	if(a[1]>1) {
		h=poly(a[1]-2,0);
		for(int i=0;i<=h.deg();i++)
			h[i]=mul(f[1][i+2],C.ifac[i]);
		h=h*g;
		for(int i=1;i<=h.deg();i++)
			ans=dec(ans,mul(h[i],C.fac[i]));
	} cout<<mul(ans,m)<<'\n';
	
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值