「SDOI2015」序列统计
Posted limit-ak-ioi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「SDOI2015」序列统计相关的知识,希望对你有一定的参考价值。
先 dp 一下,设 (f_{i,j}) 为 (i) 个数字,乘积为 (j) 的数列个数
那么显然有 (f_{i imes 2,j}=sum_{(k_0 imes k_1) operatorname{mod} m= j}f_{i,k_0} imes f_{i,k_1})
快速幂+暴力卷积,复杂度好像是 (mathcal{O}(m^3 log n)) ,不能过。
考虑这个乘法卷积咋去做。
加法卷积我们会做(即 (operatorname{NTT}) 板子),那么我们把乘法卷积转为对数搞成加法卷积的形式不就行了吗?
现在问题是如何求模意义下的对数。
设 (g) 是 (m) 的原根,那么只需把每个数 (x) 转化为 (log_g x) 即可。由于原根的性质保证每个 (log_g x) 都有整数解。
(不过 (x) 要先膜个 (m) 先,这是很显然的吧...
由于 (g^{varphi (m)} equiv 1),所以这个对数意义下的加法卷积其实要膜的不是 (m) 而是 (varphi (m))...
直接来一发快速幂+加法卷积,时间复杂度是 (mathcal{O}(mlog m log n)),非常优美,直接过了这题。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const ll mod=1004535809;
int G=3,invG=332748118;
const int N=2400000;
ll ksm(ll b,int n,int md){
ll res=1;
while(n){
if(n&1) res=res*b%md;
b=b*b%md; n>>=1;
}
return res;
}
int tr[N];
void NTT(ll *f,int n,int fl){
for(int i=0;i<n;++i)
if(i<tr[i]) swap(f[i],f[tr[i]]);
for(int p=2;p<=n;p<<=1){
int len=(p>>1);
ll w=ksm((fl==0)?G:invG,(mod-1)/p,mod);
for(int st=0;st<n;st+=p){
ll buf=1,tmp;
for(int i=st;i<st+len;++i)
tmp=buf*f[i+len]%mod,
f[i+len]=(f[i]-tmp+mod)%mod,
f[i]=(f[i]+tmp)%mod,
buf=buf*w%mod;
}
}
if(fl==1){
ll invN=ksm(n,mod-2,mod);
for(int i=0;i<n;++i)
f[i]=(f[i]*invN)%mod;
}
}
int GetRoot(int x) {
int f[10005],tot=0,phi=x-1;
for(int i=2;i*i<=phi;++i)
if(phi%i==0){
while(phi%i==0) phi/=i;
f[++tot]=i;
}
if(phi>1) f[++tot]=phi;
phi=x-1;
for(int i=2;i<=phi;++i){
int fl=1;
for(int j=1;j<=tot&&fl;++j)
if(ksm(i,phi/f[j],x)==1) fl=0;
if(fl) return i;
}
}
int m;
void Mul(ll *f,ll *g,int n){
static ll a[N],b[N];
for(int i=0;i<n;++i)
a[i]=f[i],b[i]=g[i];
NTT(a,n,0);NTT(b,n,0);
for(int i=0;i<n;++i)
a[i]=1ll*a[i]*b[i]%mod;
NTT(a,n,1);
for(int i=0;i<m-1;++i)
f[i]=(a[i]+a[i+m-1])%mod;
}
ll f[N],g[N];
map<int,int> Log;
signed main(){
invG=ksm(G,mod-2,mod);
int n,x,s,X;
cin>>n>>m>>x>>s;
int groot=GetRoot(m);
//cout<<groot<<endl;
for(int i=0;i<m-1;++i)
Log[ksm(groot,i,m)]=i;
for(int i=1;i<=s;++i){
scanf("%lld",&X);X%=m;
if(X)f[Log[X]]++;
}
X=1;
g[Log[X]]=1;
int limit=1;
while(limit<=(m<<1)) limit<<=1;
for(int i=0;i<limit;++i)
tr[i]=(tr[i>>1]>>1)|(i&1?limit>>1:0);
while(n){
if(n&1) Mul(g,f,limit);
Mul(f,f,limit);n>>=1;
}
cout<<g[Log[x]];
return 0;
}
以上是关于「SDOI2015」序列统计的主要内容,如果未能解决你的问题,请参考以下文章