「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」序列统计的主要内容,如果未能解决你的问题,请参考以下文章

P3321 [SDOI2015]序列统计(未解决)

BZOJ 3992: [SDOI2015]序列统计

[SDOI2015]序列统计

bzoj3992SDOI2015序列统计

[BZOJ3992][SDOI2015]序列统计

[SDOI2015]序列统计