码迷,mamicode.com
首页 > 其他好文 > 详细

CF1096G Lucky Tickets

时间:2020-04-25 12:48:31      阅读:51      评论:0      收藏:0      [点我收藏+]

标签:++   ret   name   过程   长度   register   scanf   turn   要求   

X.CF1096G Lucky Tickets

这题一个NTT快速幂的形式就非常明显了。直接构建一个函数\(g(x)=[x\in \text{给出的k个数码}]\)。则我们要求的就是\(\sum\limits_{i=0}^{\infty}(g^{n/2}(i))^2\)。由于模数是\(998244353\),因此采取NTT会比较轻松。

但是,如果你直接打一份暴力NTT快速幂上去,是会T的。怎么办呢?

由于\(g^1\)的范围很小(\([0,9]\)),因此如果我们一直总是把NTT的数组长度设为与\(n\)同级的长度的话,就会让前面很多东西都是在空跑。因此,我们可以在快速幂的过程中,动态修改数组长度。假如现在快速幂到\(g^x\),我们就令数组长度为一个与\(x\)同级的长度,当快速幂到\(g^{2x}\)时,再令数组长度为一个与\(2x\)同级的长度。这样,原本复杂度约为\(n\log^2n\),同时有大常数。现在,复杂度约为\(\sum_{i=1}^{\log n}2^i\log{2^i}=\sum_{i=1}^{\log n}i*2^i\approx n\log n\log\log n\)(尽管常数仍然巨大)。

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int G=3;
int n,bs[1<<20],lim=1,lg,m,invlim,rev[1<<20],f[1<<20],g[1<<20],qaq[1<<20],res;
inline int pov(int x,int y){
	int res=1;
	for(;y;x=(1ll*x*x)%mod,y>>=1)if(y&1)res=(1ll*res*x)%mod;
	return res;
}
inline void NTT(int *a,int tp){
	for(register int i=0;i<lim;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
	for(register int md=1;md<lim;md<<=1){
		register int rt=pov(G,(mod-1)/(md<<1));
		if(tp==-1)rt=pov(rt,mod-2);
		for(register int stp=md<<1,pos=0;pos<lim;pos+=stp){
			register int w=1;
			for(register int i=0;i<md;i++,w=(1ll*w*rt)%mod){
				register int x=a[pos+i],y=(1ll*w*a[pos+md+i])%mod;
				a[pos+i]=(x+y)%mod;
				a[pos+md+i]=(x-y+mod)%mod;
			}
		}
	}
}
inline void mul(int *a,int *b,int *c){
	for(register int i=0;i<lim;i++)f[i]=a[i],g[i]=b[i];
	NTT(f,1),NTT(g,1);
	for(register int i=0;i<lim;i++)f[i]=(1ll*f[i]*g[i])%mod;
	NTT(f,-1);
	for(register int i=0;i<lim;i++)c[i]=(1ll*f[i]*invlim)%mod;
}
inline void ksm(int x){
	if(x==1){while(lim<x*10)lim<<=1,lg++;for(register int i=0;i<10;i++)qaq[i]=bs[i];return;}
	ksm(x>>1);
	while(lim<x*10)lim<<=1,lg++;
	invlim=pov(lim,mod-2);
	for(register int i=1;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
	mul(qaq,qaq,qaq);
	if(x&1)mul(qaq,bs,qaq);
}
int main(){
	scanf("%d%d",&n,&m),n>>=1;
	for(register int i=0,x;i<m;i++)scanf("%d",&x),bs[x]++;
	ksm(n);
	for(register int i=0;i<lim;i++)res=(1ll*qaq[i]*qaq[i]%mod+res)%mod;
	printf("%d\n",res);
	return 0;
}

CF1096G Lucky Tickets

标签:++   ret   name   过程   长度   register   scanf   turn   要求   

原文地址:https://www.cnblogs.com/Troverld/p/12772267.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!