P4491 [HAOI2018] 染色

题目链接

题意分析

要求\(k\)种颜色恰好出现\(S\)

也就是出现\(S\)次的颜色恰好有\(k\)

我们可以发现\(lim=max\{k\}=min(\frac{n}{s},{m})\)

我们可以容斥一下 就是

出现\(S\)次的颜色至少存在\(k\)

那么

\[dp[k]=C_m^kC_{n}^{ks}(m-k)^{m-ks} \]

应该和好理解的

出现\(S\)次的颜色恰好\(k\)种就是

\[ans[k]=\sum_{j=k}^{lim}(-1)^{j-k}C_j^kdp[j] \]

\(j\)种颜色中\(k\)种被重复算了\(C_j^k\)

那么我们拆开就是

\[ans[k]* k!=\sum_{j=k}^{lim}\frac{(-1)^{j-k}}{(j-k)!}dp[j]* j! \]

如何求\(\sum_{j=k}^{lim}\frac{(-1)^{j-k}}{(j-k)!}dp[j]* j!\)

我们考虑维护出

\[a[i]=dp[i]* i!\ \ \ \ \ \ b[i]=\frac{(-1)^{i}}{i!} \]

然后就是套路 翻转\(a\)序列

那么就是多项式乘法\(NTT\)

那么对应就是\(lim-j+j-k=lim-k\)

所以我们再反转一下就可以了

然后累加就可以了

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<deque>
#include<vector>
#include<ctime>
#define ll long long
#define inf 0x7fffffff
#define N 500008
#define IL inline
#define M 10000611
#define D double
#define mod 1004535809
#define Gi 334845270 
#define R register
using namespace std;
template<typename T>IL void read(T &_)
{
    T __=0,___=1;char ____=getchar();
    while(!isdigit(____)) {if(____=='-') ___=0;____=getchar();}
    while(isdigit(____)) {__=(__<<1)+(__<<3)+____-'0';____=getchar();}
    _=___ ? __:-__;
}
/*-------------OI使我快乐-------------*/
int n,m,s,have,lim,all;
ll ans;
ll fav[M],fac[M],inv[M],num[M];
ll dp[M],cdy[M],wzy[M];
int key[M];
IL ll C(int x,int y)
{return fac[x]*fav[x-y]%mod*fav[y]%mod;}
IL ll qpow(ll x,ll y)
{ll res=1;for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod;return res;}
IL void NTT(ll *A,int knd)
{
    for(R int i=0;i<lim;++i) if(i<key[i]) swap(A[i],A[key[i]]);
    for(R int mid=1;mid<lim;mid<<=1)
    {
        ll Wn=qpow(knd==1 ? 3:Gi,(mod-1)/(mid<<1));
        for(R int j=0;j<lim;j+=(mid<<1))
        {
            ll w=1;
            for(R int k=0;k<mid;++k,w=w*Wn%mod)
            {
                ll x=A[j+k],y=w*A[j+k+mid]%mod;
                A[j+k]=(x+y)%mod;A[j+k+mid]=(x-y+mod)%mod;
            }
        }
    }
    if(knd==-1)
    {
        ll inv=qpow(lim,mod-2);
        for(R int i=0;i<lim;++i) A[i]=A[i]*inv%mod;
    }
}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
    read(n);read(m);read(s);have=min(n/s,m);
    for(R int i=0;i<=m;++i) read(num[i]),num[i]%=mod;
    fac[0]=fac[1]=fav[0]=fav[1]=inv[0]=inv[1]=1;
    for(R int i=2;i<=10000000;++i)
    {
        fac[i]=fac[i-1]*i%mod;
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        fav[i]=fav[i-1]*inv[i]%mod;
    }
//	for(R ll i=1;i<=10;++i) printf("%lld%c",fac[i],(i==10 ? '\n':' '));
    for(R int i=0;i<=have;++i)
    dp[i]=C(m,i)*C(n,i*s)%mod*fac[i*s]%mod*qpow(fav[s],i)%mod*qpow(m-i,n-i*s)%mod;
//	for(R ll i=0;i<=have;++i)
//	printf("%lld%c",dp[i],(i==have ? '\n':' '));
    for(R int i=0;i<=have;++i)
    {
        cdy[i]=(((i&1) ? -1:1)*fav[i]%mod+mod)%mod;
        wzy[i]=dp[i]*fac[i]%mod;
    } 
    reverse(wzy,wzy+have+1);
    for(lim=1;lim<=(have<<1);lim<<=1) ++all;
    for(R int i=0;i<lim;++i) key[i]=((key[i>>1]>>1)|((i&1)<<(all-1)));
    NTT(cdy,1);NTT(wzy,1);
    for(R int i=0;i<lim;++i) cdy[i]=cdy[i]*wzy[i]%mod;
    NTT(cdy,-1);
    reverse(cdy,cdy+have+1);
    for(R int i=0;i<=have;++i) ans=(ans+num[i]*cdy[i]%mod*fav[i]%mod)%mod;
    printf("%lld\n",(ans%mod+mod)%mod);
//	fclose(stdin);
//	fclose(stdout);
    return 0;
}


HEOI 2019 RP++

posted @ 2019-03-24 21:18  tcswuzb  阅读(157)  评论(0编辑  收藏  举报