• P4491 [HAOI2018] 染色


    传送门:洛谷

    解题思路:

    写本题需要知道一个前置知识:

    假设恰好选 k k k个条件的方案数为 f ( k ) f(k) f(k);先钦定选 k k k个条件,其他条件无所谓的方案数为 g ( k ) g(k) g(k)
    那么存在这样的一个关系: g ( k ) = ∑ i = k n C i k f ( i ) g(k)=\sum_{i=k}^nC_{i}^kf(i) g(k)=i=knCikf(i)
    上述式子的含义是可以枚举实际上选了几个,然后再这 i i i个中选择 k k k个作为钦定的计算方案数.因为钦定这种方式是存在重复方案的
    然后使用二项式反演可以实现钦定和恰好之间的转化.
    经过二项式反演可以得到: f ( k ) = ∑ i = k n C i k ∗ ( − 1 ) i − k ∗ g ( i ) f(k)=\sum_{i=k}^nC_{i}^{k}*(-1)^{i-k}*g(i) f(k)=i=knCik(1)ikg(i).

    对于本题来说,我们的 g ( i ) g(i) g(i)其实很容易写出.设 g ( i ) g(i) g(i)为恰好出现 i i i个的出现次数为 s s s的颜色的方案数.不难写出 g ( i ) = C m i A n s ∗ i ( s ∗ i ) ! ∗ ( m − i ) n − s ∗ i g(i)=C_m^i\frac{A_n^{s*i}}{(s*i)!}*(m-i)^{n-s*i} g(i)=Cmi(si)!Ansi(mi)nsi
    然后我们反演一下就得到了: f ( k ) = ∑ i = k n C i k ( − 1 ) i − k g ( i ) f(k)=\sum_{i=k}^{n}C_{i}^{k}(-1)^{i-k}g(i) f(k)=i=knCik(1)ikg(i)
    稍微化解一下就能得到:
    f ( k ) ∗ k ! = ∑ i = k n g ( i ) ∗ i ! ∗ ( − 1 ) i − k ( i − k ) ! f(k)*k!=\sum_{i=k}^ng(i)*i!*\frac{(-1)^{i-k}}{(i-k)!} f(k)k!=i=kng(i)i!(ik)!(1)ik
    然后我们设 G ( i ) = g ( i ) ∗ i ! , H ( i ) = ( − 1 ) i − k ( i − k ) ! G(i)=g(i)*i!,H(i)=\frac{(-1)^{i-k}}{(i-k)!} G(i)=g(i)i!,H(i)=(ik)!(1)ik就能得到 F ( k ) = ∑ i = k n G ( i ) ∗ H ( i − k ) F(k)=\sum_{i=k}^nG(i)*H(i-k) F(k)=i=knG(i)H(ik)
    我们使用经典套路将 G G G数组 r e v e r s e reverse reverse一下,就得到了 F ( K ) = ∑ i = k n G ( n − i ) ∗ H ( i − k ) F(K)=\sum_{i=k}^nG(n-i)*H(i-k) F(K)=i=knG(ni)H(ik)
    PS:需要注意的是此时翻转的n可以不为n,不熟悉的人可能会搞不清楚
    然后这是一道很显然的卷积式子.此时我们使用 N T T NTT NTT卷一下即可.此时我们的的 f ( k ) f(k) f(k)就是卷积完之后第 n − k n-k nk项的系数.


    下面是具体的代码部分:

    #include 
    using namespace std;
    typedef long long ll;
    #define root 1,n,1
    #define ls rt<<1
    #define rs rt<<1|1
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    inline ll read() {
    	ll x=0,w=1;char ch=getchar();
    	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x*w;
    }
    inline void print(__int128 x){
    	if(x<0) {putchar('-');x=-x;}
    	if(x>9) print(x/10);
    	putchar(x%10+'0');
    }
    #define maxn 10001000
    #define int long long
    const int mod=1004535809;
    const double eps=1e-8;
    #define	int_INF 0x3f3f3f3f
    #define ll_INF 0x3f3f3f3f3f3f3f3f
    int qpow(int a,int b) {
    	int ans=1;
    	while(b) {
    		if(b&1) ans=ans*a%mod;
    		b>>=1;
    		a=a*a%mod;
    	}
    	return ans;
    }
    int rev[maxn];
    void NTT(int *a,int n,int inv) {
    	for(int i=0;i<=n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    	for(int len=1;len<=(n>>1);len<<=1) {
    		int gn=qpow(inv==1?3:qpow(3,mod-2),(mod-1)/(len<<1));
    		for(int i=0;i<=n;i+=(len<<1)) {
    			int g0=1;
    			for(int j=0;j<=len-1;j++) {
    				int x=a[i+j],y=a[i+j+len]*g0%mod;
    				a[i+j]=(x+y)%mod;a[i+j+len]=((x-y)%mod+mod)%mod;
    				g0=g0*gn%mod;
    			}
    		}
    	}
    }
    int fac[maxn],in_fac[maxn];
    void init(int limit) {
    	fac[0]=1;
    	for(int i=1;i<=limit;i++) {
    		fac[i]=fac[i-1]*i%mod;
    	}
    	in_fac[limit]=qpow(fac[limit],mod-2);
    	for(int i=limit-1;i>=0;i--) {
    		in_fac[i]=in_fac[i+1]*(i+1)%mod;
    	}
    }
    int C(int a,int b) {
    	return fac[a]*in_fac[b]%mod*in_fac[a-b]%mod;
    }
    int A(int a,int b) {
    	return fac[a]*in_fac[a-b]%mod;
    }
    int w[maxn];int g[maxn];int G[maxn],H[maxn];int F[maxn];
    signed main() {
    	int n=read();int m=read();int s=read();
    	init(max(n,m));
    	for(int i=0;i<=m;i++) {
    		w[i]=read();
    	}
    	int k=min(m,n/s);
    	for(int i=0;i<=k;i++) {
    		g[i]=C(m,i)*A(n,s*i)%mod*qpow(in_fac[s],i)%mod*qpow(m-i,n-s*i)%mod;
    	}
    	for(int i=0;i<=k;i++) {
    		G[i]=g[i]*fac[i]%mod;
    		H[i]=((i&1?-1:1)*in_fac[i]%mod+mod)%mod;
    	}
    	reverse(G,G+k+1);
    	int limit=1,len=0;
    	while(limit<=k+k) limit<<=1,len++;
    	for(int i=0;i<=limit;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
    	NTT(G,limit,1);NTT(H,limit,1);
    	for(int i=0;i<=limit;i++) F[i]=G[i]*H[i]%mod;
    	NTT(F,limit,-1);
    	int inv=qpow(limit,mod-2);
    	for(int i=0;i<=limit;i++) {
    		F[i]=F[i]*inv%mod;
    	}
    	int ans=0;
    	for(int i=0;i<=k;i++) {
    		ans=(ans+F[k-i]*in_fac[i]%mod*w[i]%mod)%mod;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
  • 相关阅读:
    【超标量】分支预测的方向预测总结
    动手学深度学习——求导
    Java根据线程指标自定义HPA(Prometheus为监控收集)
    keepAlive
    基于DTU加油站数据采集系统,加油站也能实现智能化
    Linux下shell编写脚本指南
    安全加固指南(系统,数据库,中间件)
    ElasticSearch系列——Elasticsearch Java API Client
    【reverse】新160个CrackMe之154-cpp_crackme1——MFC+纯算法逆向
    Kafka从安装使用到集成Springboot详细教程
  • 原文地址:https://blog.csdn.net/yingjiayu12/article/details/133843484