LOJ 6503 「雅礼集训 2018 Day4」Magic - 容斥 - 分治NTT

题目大意:有 m m 种颜色的球每种 a i a_i 个共 n n 个,问有多少种排列使得存在恰好k对相邻的球颜色相同。n<=1e5,m<=2e4.
题解:
首先给所有球一个标号即使颜色相同也认为是不同的球,这样最后除以 a i ! \prod a_i! 即可。然后考虑容斥,设 g ( x ) g(x) 表示至少x对的答案,知道这个就可以直接拼出 a n s ( k ) ans(k)
然后考虑每一对颜色相同的相邻的求,只在后一个球处k++。那么钦定一些球和其前驱相同之后,相当于是先决定把这些球挂上去,然后那些没有被钦定的求随意排列。
前半部分设 f ( i , j ) f(i,j) 表示前i种球已经钦定了j个的方案数,那么枚举第i种球选了多少个叫做 x [ 0 , a i ) x\in[0,a_i) ,那么首先要 ( a i x ) \binom{a_i}{x} 的选出来,然后第一个球有 a i x a_i-x 种挂的位置,第二个有 a i x + 1 a_i-x+1 ,以此类推最后一个有 a i 1 a_i-1 个位置,那么 f ( i , j ) = x = 0 m i n ( j , a i 1 ) f ( i 1 , j x ) ( a i x ) ( a i 1 ) ! ( a i x 1 ) ! f(i,j)=\sum_{x=0}^{min(j,a_i-1)}f(i-1,j-x)\binom{a_i}{x}\frac{(a_i-1)!}{(a_i-x-1)!} ,发现这是个卷积,直接分治NTT即可。
最后 g ( x ) = f ( m , x ) ( n x ) ! g(x)=f(m,x)(n-x)! ,就是没被钦定的随意排列。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define p 998244353
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
inline void clr(int *a,int n) { memset(a,0,sizeof(int)*n); }
inline void cpy(int *a,int *b,int n) { memcpy(a,b,sizeof(int)*n); }
inline int *newInt(int n) { int *t=new int[n];return clr(t,n),t; }
inline int fast_pow(int x,int k,int ans=1) { for(;k;k>>=1,x=(lint)x*x%p) (k&1)?ans=(lint)ans*x%p:0;return ans; }
const int M=20010,N=140000;
namespace NTT_space{
	int *dwg[30],*dwgi[30];
	inline int prelude_dwg()
	{
		int n=N-2;
		for(int i=2,t=1;i<=n;i<<=1,t++)
		{
			dwg[t]=newInt(i),dwgi[t]=newInt(i);
			int *d=dwg[t],*di=dwgi[t];d[0]=di[0]=1;
			int w=fast_pow(3,(p-1)/i),wn=fast_pow(3,p-1-(p-1)/i);
			rep(j,1,i-1) d[j]=(lint)d[j-1]*w%p,di[j]=(lint)di[j-1]*wn%p;
		}
		return 0;
	}
	namespace NTT_SPACE{
		int r[N],ninv;
		inline int pre(int m)
		{
			int n=1,L=0;while(n<m) n<<=1,L++;
			rep(i,1,n-1) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
			return ninv=fast_pow(n,p-2),n;
		}
		inline int NTT(int *a,int n,int s)
		{
			rep(i,1,n-1) if(i<r[i]) swap(a[i],a[r[i]]);
			for(int i=2,q=1;i<=n;i<<=1,q++)
			{
				int *d=(s>0?dwg[q]:dwgi[q]);
				for(int j=0,t=i>>1,x,y;j<n;j+=i) rep(k,0,t-1)
					x=a[j+k],y=(lint)d[k]*a[j+k+t]%p,
					a[j+k]=x+y,(a[j+k]>=p?a[j+k]-=p:0),
					a[j+k+t]=x-y,(a[j+k+t]<0?a[j+k+t]+=p:0);
			}
			if(s<0) rep(i,0,n-1) a[i]=(lint)a[i]*ninv%p;
			return 0;
		}
	}using NTT_SPACE::pre;using NTT_SPACE::NTT;
	namespace TMS_space{
		int A[N],B[N];
		inline int trans(int *a,int *b,int m,int n,int s) { return cpy(a,b,m),clr(a+m,n-m),NTT(a,n,s); }
		inline int tms(int *a,int m1,int *b,int m2,int *c,int m3=-1)
		{
			if(m3<0) m3=m1+m2-1;int n=pre(m1+m2-1);
			trans(A,a,m1,n,1),trans(B,b,m2,n,1);
			rep(i,0,n-1) A[i]=(lint)A[i]*B[i]%p;
			return NTT(A,n,-1),cpy(c,A,m3),0;
		}
	}using TMS_space::tms;
}using NTT_space::tms;
int *f[M],a[M],tmp[N],g[N],fac[N],facinv[N];
inline int solve(int l,int r)
{
	if(l==r) return 0;int mid=(l+r)>>1;solve(l,mid),solve(mid+1,r);
	tms(f[l],a[l],f[mid+1],a[mid+1],tmp),delete f[l],delete f[mid+1];
	return a[l]+=a[mid+1]-1,f[l]=newInt(a[l]),cpy(f[l],tmp,a[l]),0;
}
inline int prelude(int n)
{
	rep(i,fac[0]=1,n) fac[i]=(lint)fac[i-1]*i%p;
	facinv[n]=fast_pow(fac[n],p-2);
	for(int i=n-1;i>=0;i--) facinv[i]=(i+1ll)*facinv[i+1]%p;
	return 0;
}
inline int C(int n,int m) { if(m<0||n<m) return 0;return (lint)fac[n]*facinv[m]%p*facinv[n-m]%p; }
inline int sol(int x,int s) { return (s&1)?(x?p-x:0):x; }
int main()
{
	NTT_space::prelude_dwg();
	int m=inn(),n=inn(),k=inn(),xs=1;
	prelude(n);
	rep(i,1,m)
	{
		a[i]=inn(),f[i]=newInt(a[i]),xs=(lint)xs*facinv[a[i]]%p;
		rep(j,0,a[i]-1) f[i][j]=(lint)C(a[i],j)*fac[a[i]-1]%p*facinv[a[i]-j-1]%p;
	}
	solve(1,m);
	rep(i,0,a[1]-1) g[i]=(lint)f[1][i]*fac[n-i]%p;
	int ans=0;
	rep(i,k,a[1]-1) ans+=sol((lint)g[i]*C(i,k)%p,i-k),(ans>=p?ans-=p:0);
	return !printf("%lld\n",(lint)ans*xs%p);
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/88166203
今日推荐