[bzoj5306][NTT][容斥原理]染色

7 篇文章 0 订阅
6 篇文章 0 订阅

Description

传送门

题解

upd:原来这是二项式反演来着qwq…
贴一下容斥题的一点点做法.
首先显然先求方案数 g [ i ] g[i] g[i]表示只有 i i i种出现了 S S S次的方案数
那怎么说也要求一个 f [ i ] f[i] f[i]表示至少有 i i i种出现了 S S S次的方案数
f [ i ] = C m i ∗ C n i ∗ S ∗ ( i ∗ S ) ! i n v [ S ] i ∗ ( m − i ) n − i ∗ S f[i]=C_m^i*C_n^{i*S}*\frac{(i*S)!}{inv[S]^i}*(m-i)^{n-i*S} f[i]=CmiCniSinv[S]i(iS)!(mi)niS
i n v [ S ] inv[S] inv[S]表示 S ! S! S!的逆元
我们写一下一个东西,竖行表示 g g g,横行表示 f f f
在这里插入图片描述
注意到这是一个倒三角
中间的东西表示会被算几次
那么 后面的系数是可以从上一行推下来的
要记住容斥的这种推系数做法啊
那么式子就有了
g [ i ] = ∑ j = i ( − 1 ) j − i ∗ C j i ∗ f [ j ] g[i]=\sum_{j=i} (-1)^{j-i}*C_j^i*f[j] g[i]=j=i(1)jiCjif[j]
把组合数拆开
g [ i ] = ∑ j = i ( − 1 ) j − i ∗ j ! i ! ( j − i ) ! ∗ f [ j ] g[i]=\sum_{j=i}(-1)^{j-i}*\frac{j!}{i!(j-i)!}*f[j] g[i]=j=i(1)jii!(ji)!j!f[j]
i ! i! i!移项, j ! j! j!扔到 f [ j ] f[j] f[j]里面可以知道
g [ i ] ∗ i ! = ∑ j = i ( − 1 ) j − i ( j − i ) ! ∗ f [ j ] ∗ j ! g[i]*i!=\sum_{j=i}\frac{(-1)^{j-i}}{(j-i)!}*f[j]*j! g[i]i!=j=i(ji)!(1)jif[j]j!
后面那个项把 ( − 1 ) j − i ( j − i ) ! \frac{(-1)^{j-i}}{(j-i)!} (ji)!(1)ji反项,就可以直接卷积求出来了
注意预处理的复杂度要压到 m a x ( n , m ) max(n,m) max(n,m)不然bz会跪…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int mod=1004535809;
const int MAXN=100005;
const int MAXM=10000005;

int pow_mod(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1)ret=1LL*ret*a%mod;
		a=1LL*a*a%mod;b>>=1;
	}
	return ret;
}
int A[MAXN*4],B[MAXN*4];
int R[MAXN*4],L,ln;
void NTT(int *y,int len,int on)
{
	for(int i=0;i<len;i++)if(i<R[i])swap(y[i],y[R[i]]);
	for(int i=1;i<len;i<<=1)
	{
		int wn=pow_mod(3,(mod-1)/(i*2));if(on==-1)wn=pow_mod(wn,mod-2);
		for(int j=0;j<len;j+=(i<<1))
		{
			int w=1;
			for(int k=0;k<i;k++)
			{
				LL u=y[j+k],v=1LL*y[j+k+i]*w%mod;
				y[j+k]=(u+v)%mod;
				y[j+k+i]=(u-v+mod)%mod;
				w=1LL*w*wn%mod;
			}
		}
	}
	if(on==-1)
	{
		int temp=pow_mod(len,mod-2);
		for(int i=0;i<len;i++)y[i]=1LL*y[i]*temp%mod;
	}
}
int pre[MAXM],inv[MAXM];
int C(int n,int m){return 1LL*pre[n]*inv[m]%mod*inv[n-m]%mod;}
int n,m,S,w[MAXN];
void ad(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int main()
{
	n=read();m=read();S=read();
	int lim=max(n,m);
	pre[0]=1;for(int i=1;i<=lim;i++)pre[i]=1LL*pre[i-1]*i%mod;
	inv[lim]=pow_mod(pre[lim],mod-2);
	for(int i=lim-1;i>=0;i--)inv[i]=1LL*inv[i+1]*(i+1)%mod;
	for(int i=0;i<=m;i++)w[i]=read();
	for(ln=1;ln<=(m)*2;ln<<=1)L++;
	for(int i=0;i<ln;i++)R[i]=(R[i>>1]>>1)|(i&1)<<(L-1);
	int u1=1;
	for(int i=0;i<=m;i++)
	{
		if(i*S<=n)
		{
			int u2=i*S;
			A[i]=1LL*C(m,i)*C(n,u2)%mod*pre[u2]%mod*u1%mod*pow_mod(m-i,n-u2)%mod*pre[i]%mod;
			u1=1LL*u1*inv[S]%mod;
		}
		else break;
	}
	for(int i=0;i<=m;i++)
	{
		int c1=(i&1)?(-1):(1),c2=inv[i];
		B[m-i]=1LL*c1*c2%mod;
	}
	NTT(A,ln,1);NTT(B,ln,1);
	for(int i=0;i<ln;i++)A[i]=1LL*A[i]*B[i]%mod;
	NTT(A,ln,-1);
	int ans=0;
	for(int i=m;i<=2*(m+1)-1;i++)
	{
		int u=i-m;
		int temp=1LL*A[i]*inv[u]%mod;
		ad(ans,1LL*temp*w[u]%mod);
	}
	pr2(ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值