zoukankan      html  css  js  c++  java
  • bzoj5306:[Haoi2018]染色

    传送门

    思路还是很巧妙的。

    显然能对答案产生贡献的最多颜色为(min{m,n/S})

    (f(i))为出现次数为(S)的颜色种类至少有(i)种的涂色方案

    那么

    [f(i)=inom{m}{i}frac{n!}{(S!)^i(n-iS)!}(m-i)^{n-iS} ]

    然后让我们分析一下这个式子,(inom{m}{i})很显然

    重点是(frac{n!}{(S!)^i(n-iS)!})是怎么推出来的

    我们发现对于那些恰好出现了(S)次的颜色

    首先这(iS)个位置的选法是(inom{n}{iS}=frac{n!}{(n-iS)!(iS)!})

    然后对于这(i)种颜色的排布,我们可以列出它的方案数是等于

    [frac{(iS)!}{(iS-S)!S!}frac{(iS-S)!}{(iS-2S)!S!}frac{(iS-2S)!}{(iS-3S)!S!}...\ frac{(iS)!}{S!}frac{1}{S!}frac{1}{S!}...\ frac{(iS)!}{(S!)^i} ]

    然后就可以得出来一共的方案数为(frac{n!}{(S!)^i(n-iS)!})

    然后对于剩下的((n-iS))个位置,剩下的((m-i))种颜色随便放,就是((m-i)^{n-iS})

    于是(f(i))就推出来了


    然后用一个很简单的容斥计算答案

    [ans(i)=sum_{j=i}^{min(m,n/s)}(-1)^{j-i}inom{j}{i}f(j) ]

    看到组合数就要拆

    [ans(i)=sum_{j=i}^{min(m,n/s)}(-1)^{j-i}frac{j!}{i!(j-i)!}f(j)\ i!*ans(i)=sum_{j=i}^{min(m,n/s)}frac{(-1)^{j-i}}{(j-i)!}f(j)j!\ ]

    [A(x)=frac{(-1)^x}{x!}\ B(x)=f(x)x! ]

    那么

    [i!*ans(i)=sum_{j=i}^{min(m,n/s)}A(j-i)B(j)\ i!*ans(i)=sum_{j=0}^{min(m,n/s)-i}A(j)B(j+i)\ ]

    然后显然就是将(B)给reverse一下

    [i!*ans(i)=sum_{j=0}^{min(m,n/s)-i}A(j)B(min(m,n/s)-j-i)\ ]

    显然就是一个卷积的形式了,NTT做完之后再reverse回来

    (D)是做完之后的多项式

    [ans=sum_{i=0}^{min(m,n/s)}frac{w(i)D(i)}{i!} ]

    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    void read(int &x) {
    	char ch; bool ok;
    	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
    }
    const int maxn=5e5+10,maxm=1e7+10,mod=1004535809,g=3,gi=334845270;
    #define rg register
    int tot,r[maxm],len,n,m,ans,s,w[maxm],N,f[maxm],A[maxm],B[maxm],fac[maxm],inv[maxm];
    int mul(int x,int y){return 1ll*x*y-1ll*x*y/mod*mod;}
    int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    int del(int x,int y){return x-y<0?x-y+mod:x-y;}
    int mi(int a,int b){
    	int ans=1;
    	while(b){
    		if(b&1)ans=mul(ans,a);
    		b>>=1,a=mul(a,a);
    	}
    	return ans;
    }
    void ntt(int *a,int f){
    	for(rg int i=0;i<N;i++)if(r[i]>i)swap(a[i],a[r[i]]);
    	for(rg int i=1;i<N;i<<=1){
    		int wn=mi(f?g:gi,(mod-1)/(i<<1));
    		for(rg int j=0;j<N;j+=(i<<1)){
    			int w=1;
    			for(rg int k=0;k<i;k++){
    				int x=a[j+k],y=mul(w,a[j+k+i]);
    				a[j+k]=add(x,y),a[j+k+i]=del(x,y),w=mul(w,wn);
    			}
    		}
    	}
    	if(f)return ;int inv=mi(N,mod-2);
    	for(rg int i=0;i<N;i++)a[i]=mul(a[i],inv);
    }
    int C(int x,int y){return mul(fac[x],mul(inv[y],inv[x-y]));}
    int main()
    {
    	read(n),read(m),read(s);N=min(n/s,m);
    	fac[0]=inv[0]=1;int now=max(n,m);
    	for(rg int i=1;i<=now;i++)fac[i]=mul(fac[i-1],i);
    	inv[now]=mi(fac[now],mod-2);
    	for(rg int i=now-1;i;i--)inv[i]=mul(inv[i+1],i+1);
    	for(rg int i=0;i<=m;i++)read(w[i]);
    	for(rg int i=0;i<=N;i++)
    		f[i]=mul(C(m,i),mul(fac[n],mul(inv[n-i*s],mul(mi(mi(fac[s],i),mod-2),mi(m-i,n-i*s)))));
    	for(rg int i=0;i<=N;i++)A[i]=mul((i&1?mod-1:1),inv[i]),B[i]=mul(f[i],fac[i]);
    	reverse(B,B+N+1);tot=N*2;for(N=1;N<=tot;N<<=1)len++;
    	for(rg int i=0;i<N;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
    	ntt(A,1),ntt(B,1);
    	for(rg int i=0;i<N;i++)A[i]=mul(A[i],B[i]);
    	ntt(A,0);N=tot>>1;reverse(A,A+N+1);
    	for(rg int i=0;i<=N;i++)ans=add(ans,mul(w[i],mul(A[i],inv[i])));
    	printf("%d
    ",ans);
    }
    
  • 相关阅读:
    vue-常用指令汇总
    vue-插槽和具名插槽
    vue-传值校验
    vue-动态组件
    vue-组件
    zend studio 快捷键收集
    php调试工具firephp
    zend studio插件
    MySQL各个版本区别
    PHP 中 Date 函数与实际时间相差8小时的解决方法
  • 原文地址:https://www.cnblogs.com/lcxer/p/10834908.html
Copyright © 2011-2022 走看看