JZOJ6754.【2020.07.17NOI模拟】T3(always)

Description

  • n n n种颜色,第 i i i种颜色有 c [ i ] c[i] c[i]个。
  • 对于所有 ∑ c [ i ] \sum c[i] c[i]个元素的排列,它的贡献为 ∏ 1 l k \prod \frac{1}{l_k} lk1,其中 l k l_k lk为首尾相接之后第 k k k个极大连续段的长度。
  • ( ∑ c [ i ] ) ! ∏ c [ i ] ! \frac{(\sum c[i])!}{\prod c[i]!} c[i]!(c[i])!个排列的贡献之和模998244353。
  • n ≤ 1 e 5 , ∑ c [ i ] ≤ 2 e 5 n\leq1e5,\sum c[i]\leq2e5 n1e5,c[i]2e5

Solution

  • 这是一道容斥容斥再容斥的题目。
  • 首先考虑一条链,单独考虑一个颜色的生成函数的贡献,那么这个颜色分成 j j j段的贡献就是(有 e x k = ∑ i > = 0 ( x k ) i i ! e^{xk}=\sum_{i>=0}\frac{(xk)^i}{i!} exk=i>=0i!(xk)i):
    ( e x − 1 ) j [ x c [ i ] ] = ∑ k = 0 j ( j k ) ( − 1 ) k − j k c [ i ] c [ i ] ! (e^x-1)^j[x^{c[i]}]=\frac{\sum_{k=0}^j(_j^k)(-1)^{k-j}k^{c[i]}}{c[i]!} (ex1)j[xc[i]]=c[i]!k=0j(jk)(1)kjkc[i]
  • 这样就可以卷积求出所有 j j j的贡献。
  • 考虑直接把同一种颜色的 j j j种元素当做可重元素,那么就是可重元素的排列问题,系数乘上 1 j ! \frac{1}{j!} j!1即可卷积。但是这样可能两个同一种颜色的块分到了相邻的地方,所以要再容斥。
  • 暴力枚举有 k k k个合并到了一起。
  • g k = ∑ k = 0 j − 1 ( j − 1 k ) ( − 1 ) k f j g_k=\sum_{k=0}^{j-1}(_{j-1}^k)(-1)^{k}f_j gk=k=0j1(j1k)(1)kfj。用 g k g_k gk求可重元素排列的方案数。

  • 接下来考虑有环的答案,直接考虑第一个是哪个元素,以及这个元素有多少个,那么贡献就要再乘上这个个数。
  • 同样考虑如果第一段的贡献特殊,颜色 i i i j + 1 j+1 j+1段的贡献:
    e x x ( e x − 1 ) j [ x c [ i ] ] = ∑ k = 0 j ( j k ) ( − 1 ) j − k ( k + 1 ) c [ i ] − 1 ( c [ i ] − 1 ) ! e^xx(e^x-1)^j[x^{c[i]}]=\frac{\sum_{k=0}^j(_j^k)(-1)^{j-k}(k+1)^{c[i]-1}}{(c[i]-1)!} exx(ex1)j[xc[i]]=(c[i]1)!k=0j(jk)(1)jk(k+1)c[i]1
  • 但是不能有一段放在组合之后的第一段和最后一段,所以我们要在之前可重排列的容斥的基础之上再容斥,即钦定有一段在第一段,有一段在最后一段,在原方案上减去它们再加上有一段在第一段同时有一段在最后一段。

  • 考虑第一部分是没有开头的生成函数,第二部分是有的,分别记为 F , G F,G F,G,那么用合并果子的方式合并不同的颜色, G = F 1 ∗ G 2 + G 1 ∗ F 2 , F = F 1 ∗ F 2 G=F_1*G_2+G_1*F_2,F=F_1*F_2 G=F1G2+G1F2,F=F1F2
  • 总复杂度 O ( n   l o g 2   n ) O(n\ log^2\ n) O(n log2 n)
#include
#include
#include
#include
#include
#include
#define ll long long 
#define mo 998244353
#define maxn 200005
#define maxm 525000
#define N 524288
#define clear(a) memset(a,0,sizeof(a))
#define I(x) (((x)&1)?-1:1)
using namespace std;

int n,i,j,k,c[maxn];
ll f[maxm],g[maxm],fct[maxn],invf[maxn];

void read(int &x){
	x=0; char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}

ll ksm(ll x,ll y){
	ll s=1;
	for(;y;y/=2,x=x*x%mo) if (y&1)
		s=s*x%mo;
	return s;
}

ll C(int n,int m){
	return fct[n]*invf[n-m]%mo*invf[m]%mo;
}

int lim,bt[maxm]; ll A[maxm],B[maxm],w[maxm];
int getlim(int x){
	int lim=1; while (lim<=x) lim<<=1;
	for(int i=1;i<lim;i++) bt[i]=(bt[i>>1]>>1)|((i&1)*(lim>>1));
	return lim;
}

void dft(ll *a,int sig){
	for(int i=0;i<lim;i++) if (i<bt[i]) swap(a[i],a[bt[i]]);
	for(int mid=1;mid<lim;mid<<=1){
		int d=sig*N/(mid<<1),s=(sig<0)?N:0;
		for(int j=0;j<lim;j+=mid<<1){
			for(int k=0,p=s;k<mid;k++,p+=d){
				ll x=a[j+k],y=a[j+k+mid]*w[p];
				a[j+k]=(x+y)%mo,a[j+k+mid]=(x-y)%mo;
			}
		}
	}
	if (sig<0){
		ll inv=ksm(lim,mo-2);
		for(int i=0;i<lim;i++) a[i]=a[i]*inv%mo;
	}
}

void multi(ll *a,ll *b){
	dft(a,1),dft(b,1);
	for(int i=0;i<lim;i++) b[i]=a[i]*b[i]%mo;
	dft(b,-1);
}

int tot;
vector<ll> F[maxn],G[maxn];
ll f1[maxm],g1[maxm],f2[maxm],g2[maxm],f0[maxm],g0[maxm];
struct arr{int i,s;arr(int _i=0,int _s=0){i=_i,s=_s;}};
int operator<(arr a,arr b){return a.s>b.s;}
priority_queue<arr> Q;

int main(){
//	freopen("ceshi.in","r",stdin);
	freopen("always.in","r",stdin);
	freopen("always.out","w",stdout);
	fct[0]=1;for(i=1;i<maxn;i++) fct[i]=fct[i-1]*i%mo;
	invf[maxn-1]=ksm(fct[maxn-1],mo-2);
	for(i=maxn-2;i>=0;i--) invf[i]=invf[i+1]*(i+1)%mo;
	w[0]=1,w[1]=ksm(3,(mo-1)/N);
	for(i=2;i<=N;i++) w[i]=w[i-1]*w[1]%mo;
	
	read(n);
	for(i=1;i<=n;i++) read(c[i]);
	if (n==1) {printf("%lld",invf[c[1]]);return 0;}
	for(int t=1;t<=n;t++){
		int m=c[t];
		lim=getlim(2*m);
		memset(A,0,sizeof(ll)*lim);
		memset(B,0,sizeof(ll)*lim);
		for(i=0;i<=m;i++) 
			A[i]=invf[i]*ksm(i,m)%mo,B[i]=invf[i]*I(i)*invf[m]%mo;
		multi(A,B);
		for(i=1;i<=m;i++) f[i]=B[i]*fct[i]%mo;
		memset(A,0,sizeof(ll)*lim);
		memset(B,0,sizeof(ll)*lim);
		for(i=1;i<=m;i++) A[i]=f[i]*fct[i-1]%mo;
		for(i=0;i<=m;i++) B[m-i]=invf[i]*I(i);
		multi(A,B);
		for(i=1;i<=m;i++) g[i]=B[m+i]*invf[i-1]%mo;
		F[t].push_back(0);
		for(i=1;i<=m;i++) F[t].push_back(g[i]*invf[i]%mo);
		
		memset(A,0,sizeof(ll)*lim);
		memset(B,0,sizeof(ll)*lim);
		for(i=0;i<m;i++) 
			A[i]=invf[i]*ksm(i+1,m-1)%mo,B[i]=invf[i]*I(i)*invf[m-1]%mo;
		multi(A,B);
		for(i=0;i<m;i++) f[i]=B[i]*fct[i]%mo; f[m]=0;
		memset(A,0,sizeof(ll)*lim);
		memset(B,0,sizeof(ll)*lim);
		for(i=1;i<m;i++) A[i]=fct[i-1]*f[i]%mo;
		for(i=0;i<=m;i++) B[m-i]=invf[i]*I(i);
		multi(A,B);
		g[m+1]=g[m+2]=0;
		for(g[0]=f[0],i=1;i<=m;i++) 
			g[i]=B[m+i]*invf[i-1]%mo;
		for(i=0;i<=m;i++) g[i]=(g[i]-2*g[i+1]+g[i+2])%mo;
		for(i=0;i<=m;i++) G[t].push_back(g[i]*invf[i]%mo);
		Q.push(arr(t,m));
	}
	tot=n;
	while (Q.size()>1){
		arr t1=Q.top(); Q.pop();
		arr t2=Q.top(); Q.pop();
		lim=getlim(t1.s+t2.s);
		memset(f1,0,sizeof(ll)*lim);
		memset(g1,0,sizeof(ll)*lim);
		memset(f2,0,sizeof(ll)*lim);
		memset(g2,0,sizeof(ll)*lim);
		for(i=0;i<F[t1.i].size();i++) f1[i]=F[t1.i][i];
		for(i=0;i<G[t1.i].size();i++) g1[i]=G[t1.i][i];
		for(i=0;i<F[t2.i].size();i++) f2[i]=F[t2.i][i];
		for(i=0;i<G[t2.i].size();i++) g2[i]=G[t2.i][i];
		memset(f0,0,sizeof(ll)*lim);
		memset(g0,0,sizeof(ll)*lim);
		dft(f1,1),dft(g1,1),dft(f2,1),dft(g2,1);
		for(i=0;i<lim;i++) {	
			g0[i]=(f1[i]*g2[i]%mo+g1[i]*f2[i]%mo)%mo;
			f0[i]=f1[i]*f2[i]%mo;
		}
		dft(g0,-1),dft(f0,-1);
		k=t1.s+t2.s;
		for(i=0;i<=t2.s;i++)
			F[t2.i][i]=f0[i],G[t2.i][i]=g0[i];
		for(i=t2.s+1;i<=k;i++){
			F[t2.i].push_back(f0[i]);
			G[t2.i].push_back(g0[i]);
		}
		Q.push(arr(t2.i,k));
	}
	ll ans=0; arr t=Q.top();
	for(i=0;i<G[t.i].size();i++)
		(ans+=G[t.i][i]*fct[i]%mo)%=mo;
	printf("%lld",(ans+mo)%mo);
}

你可能感兴趣的:(题解,生成函数,FFT和NTT,生成函数,容斥,计数,NTT)