zoukankan      html  css  js  c++  java
  • 省选测试47 T2宝石(P5401 [CTS2019]珍珠) 题解

    算法一

    定义 (f[i][j]) 表示前 (i) 个珍珠,个数为奇数的种类有 (j) 种,这样状态是 (n*D) 的,转移是 (O(1)) 的,直接讨论当前选的的颜色是在奇数的 (j) 种中还是偶数的 ((D-j)) 种中

    答案即为 (sum_{i=0}^{n-m*2}f[n][i])

    复杂度 (O(n*D)) ,期望得分 (52)

    算法二

    (D leq 300,n,m leq 10^9)

    按照算法一的定义,矩阵快速幂+极致卡常

    这一部分复杂度是 (O(D^3*logn)) ,结合算法一期望得分 (60)

    算法三

    特判 (m=0) 的,(ans=D^n)(m=1) 的, (ans=D^n-A_D^n)

    这一部分复杂度 (O(1)) ,结合算法一,二,三,期望得分 (64)

    算法四

    二项式反演+生成函数

    (n-m*2) 即为合法情况下,最多有几种宝石的个数为奇数

    首先特判掉 (n-m*2<0,ans=0)(n-m*2geq D,ans=D^n)

    定义 (f_i) 表示 (D) 种宝石,恰好 (i) 种个数为奇数的方案数

    (g_i) 表示 (D) 种宝石,钦定 (i) 种个数为奇数,其余的任意选的方案数(至少 (i) 种为奇数)

    对于一种颜色, (cnt) 为奇数的方案数对应的 (EGF)(frac{e^x-e^{-x}}{2})(cnt) 任意的方案数对应的 (EGF)(e^x)

    [g_i=inom{D}{i}n![x^n](frac{e^x-e^{-x}}{2})^i(e^x)^{D-i} ]

    [g_i=inom{D}{i}frac{n!}{2^i}[x^n](e^x-e^{-x})^i(e^x)^{D-i} ]

    二项式定理展开 ((e^x-e^{-x})^i)

    [g_i=inom{D}{i}frac{n!}{2^i}[x^n]sum_{j=0}^{i}(e^x)^j(-e^{-x})^{i-j}(e^x)^{D-i} ]

    提出 (-1) 然后合并 (e) 的幂

    [g_i=inom{D}{i}frac{n!}{2^i}[x^n]sum_{j=0}^{i}(-1)^{i-j}(e^x)^{D-2(i-j)} ]

    ((e^x)^{D-2(i-j)}) (系数为 (frac{[D-2(i-j)]^n}{n!}) )和组合数展开

    [g_i=frac{D!}{i!(D-i)!}frac{n!}{2^i}sum_{j=0}^{i}frac{i!}{j!(i-j)!}(-1)^{i-j}frac{[D-2(i-j)]^n}{n!} ]

    [g_i=frac{D!}{(D-i)!}frac{1}{2^i}sum_{j=0}^{i}frac{1}{j!(i-j)!}(-1)^{i-j}[D-2(i-j)]^n ]

    换个枚举量,枚举 (i-j)

    [g_i=frac{D!}{(D-i)!}frac{1}{2^i}sum_{j=0}^{i}frac{1}{j!(i-j)!}(-1)^{j}(D-2j)^n ]

    (sum) 后面就是卷积的形式,直接 (NTT) 算就好了

    得到了 (g),然后需要二项式反演求出 (f),但是不能直接 (2)(for) 循环去求,那样是 (O(d^2))

    注意反演的式子

    [f_i=sum_{j=i}^{D}inom{j}{i}(-1)^{j-i}g_j ]

    [f_i=frac{1}{i!}sum_{j=i}^{D}frac{j!}{(j-i)!}(-1)^{j-i}g_j ]

    [f_i=frac{1}{i!}sum_{j=i}^{D}frac{(-1)^{j-i}}{(j-i)!}j!g_j ]

    后面又是一个卷积,但是是差一定,所以枚举 (j)(0)(D-i)(NTT) 求出 (Ans_{D-i}=sum_{j=0}^{D-i}A_jB_{D-i-j})

    其中 (A_i=frac{(-1)^i}{i!})(B_{D-i}=i!g_i)

    那么 (f_i=frac{1}{i!}Ans_{D-i})

    [ans=sum_{i=0}^{n-m*2}f_i ]

    复杂度 (O(DlogD)) ,期望得分 (100)

    代码
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int maxn=1<<18|10,mod=998244353,Gi=332748118,G=3;
    int n,m,D;
    int rev[maxn],a[maxn],b[maxn],f[maxn],g[maxn],fac[maxn],fy[maxn];
    
    int fp(int a,int x,int ans=1){
    	for(;x;x>>=1,a=1ll*a*a%mod)	if(x&1) ans=1ll*ans*a%mod;
    	return ans;
    }
    
    void NTT(int *a,int lim,int opt){
    	for(int i=0;i<lim;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
    	for(int d=1;d<lim;d<<=1){
    		int st=d<<1,w1=fp(opt==-1?Gi:G,(mod-1)/st);
    		for(int i=0;i<lim;i+=st){
    			int w=1;
    			for(int j=0;j<d;++j,w=1ll*w*w1%mod){
    				int x=a[i+j],y=1ll*w*a[i+j+d]%mod;
    				a[i+j]=(x+y)%mod;
    				a[i+j+d]=(x+mod-y)%mod;
    			}
    		}
    	}
    	if(opt!=-1) return;
    	for(int i=0,inv=fp(lim,mod-2);i<lim;++i) a[i]=1ll*a[i]*inv%mod;
    }
    
    void Init(int n){
    	fac[0]=fy[0]=1;
    	for(int i=1;i<=n;++i) fac[i]=1ll*fac[i-1]*i%mod;
    	fy[n]=fp(fac[n],mod-2);
    	for(int i=n;i>1;--i) fy[i-1]=1ll*fy[i]*i%mod;
    }
    
    int main(){
    	scanf("%d%d%d",&D,&n,&m);
    	Init(D);
    	if(n-2*m<0) return puts("0"),0;
    	if(n-2*m>=D) return printf("%d
    ",fp(D,n)),0;
    	
    	for(int i=0;i<=D;++i){
    		if(i&1) a[i]=1ll*(mod-fp((D-2*i+mod)%mod,n))*fy[i]%mod;
    		else a[i]=1ll*fp((D-2*i+mod)%mod,n)*fy[i]%mod;
    		b[i]=fy[i];
    	}
    	int lim=1,bit=0;
    	while(lim<=D*2) lim<<=1,++bit; --bit;
    	for(int i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<bit);
    	NTT(a,lim,1),NTT(b,lim,1);
    	for(int i=0;i<lim;++i) a[i]=1ll*a[i]*b[i]%mod;
    	NTT(a,lim,-1);
    	for(int i=0;i<=D;++i) g[i]=1ll*fac[D]*fy[D-i]%mod*fp(fp(2,i),mod-2)%mod*a[i]%mod;
    	
    	for(int i=0;i<=D;++i){
    		if(i&1) a[i]=mod-fy[i];
    		else a[i]=fy[i];
    		b[D-i]=1ll*fac[i]*g[i]%mod;
    	}
    	for(int i=D+1;i<lim;++i) a[i]=b[i]=0;
    	NTT(a,lim,1),NTT(b,lim,1);
    	for(int i=0;i<lim;++i) a[i]=1ll*a[i]*b[i]%mod;
    	NTT(a,lim,-1);
    	for(int i=0;i<=D;++i) f[i]=1ll*a[D-i]*fy[i]%mod;
    	
    	int ans=0;
    	for(int i=0;i<=n-2*m;++i) (ans+=f[i])%=mod;
    	printf("%d
    ",(ans+mod)%mod);
    	return 0;
    }
    
  • 相关阅读:
    森林 BZOJ 3123
    calc BZOJ 2655
    修路 BZOJ 4774
    无聊的计算器【数论多合一】
    矩阵乘法 BZOJ 2738
    K大数查询 BZOJ 3110
    发展城市 BZOJ 3700
    降雨量 BZOJ 1067
    chrome中showModalDialog解决方案
    MFC webbrowser读取文档的meta分析
  • 原文地址:https://www.cnblogs.com/Lour688/p/14575165.html
Copyright © 2011-2022 走看看