zoukankan      html  css  js  c++  java
  • Biggest Set Ever

    XXXIV.Biggest Set Ever

    可能看不到题,简洁给一下题意:

    \(\prod\limits_{i=0}^{T-1}(1+x^i)\) 中所有次数 \(\equiv m\pmod{n}\) 的项的系数之和。

    数据范围:\(0\leq m<n\leq10000,1\leq T<10^{100000}\)

    首先,可以发现,因为次数对 \(n\) 取模(相当于循环卷积),故实际上对于 \(i<T\bmod n\) ,出现了 \(\left\lceil\dfrac{T}{n}\right\rceil\)\((1+x^i)\) 的项;对于剩余的部分,出现了 \(\left\lfloor\dfrac{T}{n}\right\rfloor\)\((1+x^i)\) 的项;

    于是我们令 \(p=T\bmod n,q=\left\lfloor\dfrac{T}{n}\right\rfloor\)。则,原式等价于\(\Big(\prod\limits_{i=0}^{n-1}(1+x^i)\Big)^q\prod\limits_{i=0}^{p-1}(1+x^i)\)

    两边括号里的东西都可以 \(O(n^2)\) 地暴力乘出来(这里吐槽一下数据范围,\(n^2\) 跑一万会让人觉得比较不寻常)。然后,左边括号里的东西上一个多项式快速幂即可。

    需要注意的是,常规卷积意义下的多项式快速幂的模数(设为 \(P\)),在指数上的模数也是 \(P\) 本身;然而,循环卷积意义下的模数,是有不同的表现的。

    考虑 \(\Big(\prod\limits_{i=0}^{n-1}(1+x^i)\Big)^P\)。考虑其中某一项 \((1+x^i)^P\)。考虑二项式展开得到 \(\sum\limits_{j=0}^P\dbinom{P}{j}x^{ij}\)。因为所有系数在 \(P\) 意义下取模,所以只需保留 \(\dbinom{P}{0}\)\(\dbinom{P}{P}\),其它东西在模意义下都是 \(0\)。故有 \((1+x^i)^P=1+x^{iP}\)。因为 \(P\)\(n\) 互质,所以 \(\prod\limits_{i=0}^{n-1}(1+x^{iP})\),就等价于 \(\prod\limits_{i=0}^{n-1}(1+x^i)\)——\(P\)\(0\sim n-1\) 倍刚好取到 \(0\sim n-1\) 中所有元素(扩展欧拉定理)。

    于是我们只需要关于 \(P-1\) 取模就行了。需要注意的是,当被模的数非零且模完后恰为 \(0\) 时,其应该被当作 \(P-1\) 看待——因为,我们只证明了 \(\Big(\prod\limits_{i=0}^{n-1}(1+x^i)\Big)^P=\prod\limits_{i=0}^{n-1}(1+x^i)\),而并没有证明 \(\Big(\prod\limits_{i=0}^{n-1}(1+x^i)\Big)^{P-1}=1\)——事实上,二者在大多数时候都是不等的!

    代码(最后的注释是样例):

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    const int G=3;
    int ksm(int x,int y){
    	int rt=1;
    	for(;y;x=(1ll*x*x)%mod,y>>=1)if(y&1)rt=(1ll*rt*x)%mod;
    	return rt;
    }
    int lim=1,LG,invlim,rev[40100];
    void NTT(int *a,int tp){
    	for(int i=0;i<lim;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
    	for(int md=1;md<lim;md<<=1){
    		int rt=ksm(G,(mod-1)/(md<<1));
    		if(tp==-1)rt=ksm(rt,mod-2);
    		for(int stp=md<<1,pos=0;pos<lim;pos+=stp){
    			int w=1;
    			for(int i=0;i<md;i++,w=(1ll*w*rt)%mod){
    				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;
    			}
    		}
    	}
    	if(tp==-1)for(int i=0;i<lim;i++)a[i]=(1ll*a[i]*invlim)%mod;
    }
    int n,m,p,q,f[10010],g[10010],h[10010];
    int A[40100],B[40100];
    void mul(int *a,int *b,int *c){
    	for(int i=0;i<n;i++)A[i]=a[i],B[i]=b[i],c[i]=0;
    	NTT(A,1),NTT(B,1);
    	for(int i=0;i<lim;i++)A[i]=1ll*A[i]*B[i]%mod;
    	NTT(A,-1);
    	for(int i=0;i<lim;i++)(c[i%n]+=A[i])%=mod,A[i]=B[i]=0;
    }
    void read(){
    	char c=getchar();
    	bool over=false;
    	while(c>'9'||c<'0')c=getchar();
    	while(c>='0'&&c<='9'){
    		p=(p<<3)+(p<<1)+(c^48),q=(10*q+(p/n))%(mod-1);
    		if(p>=n)over=true;
    		p%=n,c=getchar();
    	}
    	if(over&&!q)q=mod-1;
    }
    void ksm(int *a,int *b,ll y){
    	for(int i=0;i<n;i++)b[i]=0;b[0]=1;
    	for(;y;y>>=1,mul(a,a,a))if(y&1)mul(a,b,b);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	read();
    	while(lim<=(n<<1))lim<<=1,LG++;invlim=ksm(lim,mod-2);
    	for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(LG-1));
    	f[0]=1;
    	for(int i=0;i<p;i++){
    		for(int j=0;j<n;j++)h[j]=f[j];
    		for(int j=0;j<n;j++)(h[(j+i)%n]+=f[j])%=mod;
    		for(int j=0;j<n;j++)f[j]=h[j];
    	}
    	g[0]=1;
    	for(int i=0;i<n;i++){
    		for(int j=0;j<n;j++)h[j]=g[j];
    		for(int j=0;j<n;j++)(h[(j+i)%n]+=g[j])%=mod;
    		for(int j=0;j<n;j++)g[j]=h[j];
    	}
    	ksm(g,h,q);
    	mul(h,f,g);
    	printf("%d\n",g[m]);
    	return 0;
    }
    /*
    3 2 5
    1 0 20
    */
    

  • 相关阅读:
    初始化ArrayList的两种方法
    MySQL最大连接数设置
    页面按钮的语义和可访问性
    H5+App开发框架汇总
    JS使用模板快速填充HTML控件数据
    Meta标签中的format-detection属性及含义
    java中@Qualifier("string")是什么用法
    MySQL 当记录不存在时insert,当记录存在时update
    美国40岁以上的程序员在干啥
    老程序员都去哪了?
  • 原文地址:https://www.cnblogs.com/Troverld/p/14610720.html
Copyright © 2011-2022 走看看