• CF1096G Lucky Tickets(NTT)


    CF1096G Lucky Tickets

    前言

    挺简单的一道题,NTT 入门。

    对于之后理解生成函数应该有很大帮助。

    解法

    先想想这个计数怎么计。

    设选 \(\frac{n}{2}\) 个数和为 \(s\) 的方案数为 \(f_s\)。那么最后我们求得答案就是:

    \[\sum\limits_{s=0}^\frac{9n}{2} {f_s}^2 \]

    这个读者自证不难

    那么问题来了,怎样快速求出 \(f_s\)?我们可以想到构造多项式来解决。

    比如对于样例一:

    4 2
    1 8
    

    我们可以构造一个多项式 \(g(x)=x+x^8\)。也就是,把每个可以选用的数字,作为次数的那一项前的系数设为 \(1\),其余为 \(0\)

    这样有什么好处呢?

    再来看这个样例,我们求的是选择 \(2\) 个数和为 \(s\) 的方案数。那么我们就将 \(g(x)\) 平方,得到 \(x^2+2x^9+x^{16}\),这样就代表 \(f_2=1,f_9=2,f_{16}=1\)

    这其实是用到了多项式乘法次数相加,系数相乘的性质。这个相加也就是我们的数字相加。因此这样计算是可以的。

    所以最后归纳一下,我们要做以下步骤:

    1. 构造 \(g(x)\)
    2. 计算 \(h(x)=g(x)^{\frac{n}{2}}\)
    3. 根据公式求出 \(ans\)

    代码

    不会有人想用 FFT 吧,不会吧,不会吧。

    小丑竟是我自己。

    //Don't act like a loser.
    //This code is written by huayucaiji
    //You can only use the code for studying or finding mistakes
    //Or,you'll be punished by Sakyamuni!!!
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    
    int read() {
    	char ch=getchar();
    	int f=1,x=0;
    	while(ch<'0'||ch>'9') {
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return f*x;
    }
    
    const int MAXN=3e6+10;
    const int G=3;
    const int MOD=998244353;
    
    int n,k;
    int f[MAXN],r[MAXN];
    
    
    int qpow(int x,int y) {
    	int ret=1;
    	while(y) {
    		if(y&1) {
    			ret=x*ret%MOD;
    		}
    		y>>=1;
    		x=x*x%MOD;
    	}
    	return ret;
    }
    
    void NTT(int A[],int d,int inv) {
    	for(int i=0;i<d;i++) {
    		if(i<r[i]) {
    			swap(A[i],A[r[i]]);
    		}
    	}
    	
    	for(int len=1;len<d;len<<=1) {
    		int gn=qpow(G,(MOD-1)/(len*2));
    		for(int i=0;i<d;i+=len<<1) {
    			int g=1;
    			for(int k=0;k<len;k++,g=g*gn%MOD) {
    				int x=A[i+k];
    				int y=g*A[i+k+len]%MOD;
    				A[i+k]=(x+y)%MOD;
    				A[i+k+len]=(x-y+MOD)%MOD;
    			}
    		}
    	}
    	
    	if(inv==-1) {
    		reverse(A+1,A+d);
    		int g=qpow(d,MOD-2);
    		for(int i=0;i<d;i++) {
    			A[i]=A[i]*g%MOD;
    		}
    	}
    }
    
    signed main() {
    	n=read();k=read();
    	while(k--) {
    		f[read()]=1;
    	}
    	
    	int d=1,bit=0;
    	while(d<=n/2*9) {
    		d<<=1;
    		bit++;
    	}
    	for(int i=0;i<d;i++) {
    		r[i]=(r[i>>1]>>1)|((i&1)<<(bit-1));
    	}
    	
    	NTT(f,d,1);
    	for(int i=0;i<d;i++) {
    		f[i]=qpow(f[i],n/2);
    	}
    	NTT(f,d,-1);
    	
    	int ans=0;
    	for(int i=0;i<d;i++) {
    		ans=(ans+f[i]*f[i]%MOD)%MOD;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
    
  • 相关阅读:
    【Selenium IDE】下载安装Chrome和Firefox插件IDE ide了解就行 不是重点 重点是写脚本
    调用接口时,生产环境,路径加斜杠“/”和不加的区别
    WPF 踩坑笔记12 DataGrid触发选中行事件
    WPF 踩坑笔记11 线程取消
    WPF 踩坑笔记10 ListBox异步动态加载
    WPF 踩坑笔记9 直接打印
    思维的体操
    【洛谷 P4213】 【模板】杜教筛(Sum)
    【洛谷 P2257】 YY的GCD(莫比乌斯反演)
    【洛谷 P4980】 【模板】Pólya 定理
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/CF1096G.html
Copyright © 2020-2023  润新知