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;
    }
    
  • 相关阅读:
    golang 数据结构 优先队列(堆)
    leetcode刷题笔记5210题 球会落何处
    leetcode刷题笔记5638题 吃苹果的最大数目
    leetcode刷题笔记5637题 判断字符串的两半是否相似
    剑指 Offer 28. 对称的二叉树
    剑指 Offer 27. 二叉树的镜像
    剑指 Offer 26. 树的子结构
    剑指 Offer 25. 合并两个排序的链表
    剑指 Offer 24. 反转链表
    剑指 Offer 22. 链表中倒数第k个节点
  • 原文地址:https://www.cnblogs.com/Lour688/p/14575165.html
Copyright © 2011-2022 走看看