zoukankan      html  css  js  c++  java
  • 洛谷P4491 [HAOI2018]染色

    题面
    题解:首先这个“恰好”看着很不爽,换成“至少”。
    (f[i])表示颜色个数为(S)的颜色至少有(i)个的方案数。
    考虑如何计算。
    1.(m)个颜色选了(i)个,(inom{m}{i})
    2.(i)个颜色选了恰好(S)个,其他的(m-i)个颜色任选;$$frac {n!}{ {S!}^i imes fac[n-i*S]} $$
    3.剩下(n-i*S)个位置任选(m-i)中颜色,({m-i}^{n-i*S})
    把它们乘起来就是(f[i])
    接下来考虑计算答案。
    考虑容斥。设(g[i])为颜色个数为(S)的颜色恰好有(i)个的方案数。于是有:

    [g[i]= sum_{j=i}^{lim} frac { {-1}^{j-i} } {fac[j-i]} imes f[j] imes inom{j}{i} ]

    这个(lim)表示的是最多能取到的恰好有(S)个的颜色数,(lim=min(n/S,m))
    将组合数拆开,试着把式子化为卷积的形式。

    [g[i] imes fac[i] = sum_{j=i}^{lim} frac { {-1}^{j-i} } {fac[j-i]} imes f[j] imes fac[j] ]

    这样用NTT做就好了。
    令$$a[i]=f[i] imes fac[i],b[i]= frac { {-1}^{j-i} } {fac[j-i]} $$
    (b)数组翻转一下,取做完卷积的数组的(lim)(2lim)位更新答案即可。
    时间复杂度:(O(n+mlogm))
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline int
    #define Cl(x,y) memset(x,y,sizeof(x))
    #define kTk system("pause")
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    const int Mod=1004535809;
    int n,m,k,S,len,lim,ans,r[303000],w[303000],fac[10100000],inv[10100000],f[303000],A[303000],B[303000];
    I Add(int &x,int y){
    	x+=y;if(x>=Mod)x-=Mod;
    }
    IN Plus(int x,int y){
    	x+=y;if(x>=Mod)x-=Mod;return x;
    }
    IN Pow(int x,int y){
    	re res=1;
    	while(y){
    		if(y&1)res=(ll)res*x%Mod;
    		x=(ll)x*x%Mod;
    		y>>=1;
    	}
    	return res;
    }
    IN C(int x,int y){
    	return (ll)fac[x]*inv[y]%Mod*inv[x-y]%Mod;
    }
    I init(){
    	re M=10001000;
    	fac[0]=1;
    	F(i,1,M)fac[i]=(ll)fac[i-1]*i%Mod;
    	inv[M]=Pow(fac[M],Mod-2);
    	FOR(i,M-1,0)inv[i]=(ll)inv[i+1]*(i+1)%Mod;
    }
    I ntt(int *a,int sn){
    	F(i,1,S)if(i<r[i])swap(a[i],a[r[i]]);
    	for(re i=1;i<S;i<<=1){
    		re gn=Pow(3,(Mod-1)/(i<<1));if(sn==-1)gn=Pow(gn,Mod-2);
    		for(re p=i<<1,j=0;j<S;j+=p){
    			re g0=1;
    			for(re k=0;k<i;k++,g0=(ll)g0*gn%Mod){
    				re X=a[j+k],Y=(ll)a[i+j+k]*g0%Mod;
    				a[j+k]=Plus(X,Y);a[i+j+k]=Plus(X,Mod-Y);
    			}
    		}
    	}
    	if(sn==-1){
    		re invn=Pow(S,Mod-2);
    		F(i,0,S-1)a[i]=(ll)a[i]*invn%Mod;
    	}
    }
    int main(){
    	read(n);read(m);read(k);lim=min(n/k,m);
    	F(i,0,m)read(w[i]);
    	S=1;len=0;
    	while(S<((lim<<1)+2))S<<=1,len++;
    	init();
    	F(i,0,lim)f[i]=(ll)C(m,i)*fac[n]%Mod*Pow(inv[k],i)%Mod*inv[n-i*k]%Mod*Pow(m-i,n-i*k)%Mod;
    	F(i,0,S-1){
    		A[i]=(ll)f[i]*fac[i]%Mod;
    		B[i]=inv[lim-i];if((lim-i)&1)B[i]=(Mod-B[i])%Mod;
    		r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
    	}
    	ntt(A,1);ntt(B,1);
    	F(i,0,S-1)A[i]=(ll)A[i]*B[i]%Mod;
    	ntt(A,-1);
    	F(i,lim,lim<<1)Add(ans,(ll)A[i]*w[i-lim]%Mod*inv[i-lim]%Mod);
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    HDU 2643 Rank:第二类Stirling数
    HDU 4372 Count the Buildings:第一类Stirling数
    HDU 3625 Examining the Rooms:第一类Stirling数
    HDU 3682 To Be an Dream Architect:查重【三维坐标系中点在实数上的映射】
    POJ 3311 Hie with the Pie:TSP(旅行商)【节点可多次经过】
    bzoj 1050 旅行comf
    luogu 3958 奶酪
    luogu 3952 时间复杂度
    luogu 3951 小凯的疑惑
    bzoj 1016 最小生成树计数
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/12187893.html
Copyright © 2011-2022 走看看