zoukankan      html  css  js  c++  java
  • [SDOI2015]序列统计

    题目

    (S)个不同的数构成的序列(每个数可以使用任意次数),求有多少个不同的长度为(n)的序列,满足它们的乘积在模(m)意义下为(x),答案对1004535809取模

    思路

    (f(i,j))表示填了前(i)个数字,模(m)(j)的方案数,推状态转移方程的时候可以发现第一维完全没有必要一步一步走,即

    [f(i+j,C) = prod_{A imes B=C}f(i,A) imes f(j,B) ]

    可以运用类似快速幂的方法,将第一维压成(logn),时间复杂度为(O(m^2logn))

    第二维是(A+B=C)就可以卷积了,考虑用原根将乘法变成加法,即(A imes B = C,(mod) (M))变成(g^{log_gA+ log_gB}equiv g^{log_gC},(mod) (M)),进而有(log_gA+log_gBequiv log_gC,(mod) (varphi(M)))

    那么对所有数取个(log_g),就可以在模(varphi(M))意义下进行加法卷积了

    Code

    #include<bits/stdc++.h>
    #define N 400005
    using namespace std;
    typedef long long ll;
    const ll mod = 1004535809;
    int n,m,x,s,mp[8005];
    int r[N],len,L;
    ll a[N],b[N];
    ll p[N],q[N];
    ll G=3,Gi,inv,g;
    
    template <class T> inline T Max(T a,T b) { return a > b ? a : b; }
    template <class T> inline T Min(T a,T b) { return a < b ? a : b; }
    template <class T> void read(T &x)
    {
    	char c;int sign=1;
    	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
    }
    ll qp(ll a,ll b,ll mo) { ll ret=1; for(;b;b>>=1,a=a*a%mo) if(b&1) ret=ret*a%mo; return ret; }
    int get_g(int m)
    {
    	for(int i=2;i<=m-1;++i)
    	{
    		int x=m-1,ok=1;
    		for(int j=2;j*j<=x;++j)
    		{
    			if(x%j==0)
    			{
    				if(qp(i,(m-1)/j,m)==1) ok=0;
    				while(x%j==0) x/=j;
    			}
    		}
    		if(x!=1&&x!=m-1) if(qp(i,(m-1)/x,m)==1) ok=0;
    		if(ok) return i;
    	}
    	return -1;
    }
    void NTT(ll *a,int opt)
    {
    	for(int i=0;i<len;++i) if(i<r[i]) swap(a[i],a[r[i]]);
    	for(int i=1;i<len;i<<=1)
    	{
    		ll wn=qp(opt==1 ? G : Gi , (mod-1)/(i<<1),mod);
    		for(int j=0;j<len;j+=(i<<1))
    		{
    			ll w=1;
    			for(int k=0;k<i;++k,w=w*wn%mod)
    			{
    				ll x=a[j+k],y=a[j+k+i]*w%mod;
    				a[j+k]=(x+y)%mod;
    				a[j+k+i]=(x-y+mod)%mod;
    			}
    		}
    	}
    	if(opt==-1)
    	{
    		inv=qp(len,mod-2,mod);
    		for(int i=0;i<len;++i) a[i]=a[i]*inv%mod;
    	}
    }
    void mul(ll *a,ll *b,ll *c)
    {
    	for(int i=0;i<m-1;++i) p[i]=a[i],q[i]=b[i];
    	for(int i=m-1;i<len;++i) p[i]=q[i]=0;
    	NTT(p,1);NTT(q,1);
    	for(int i=0;i<len;++i) p[i]=p[i]*q[i]%mod;
    	NTT(p,-1);
    	for(int i=0;i<m-1;++i) c[i]=(p[i] + p[i+m-1])%mod;
    }
    
    int main()
    {
    	read(n);read(m);read(x);read(s);
    	g=get_g(m); Gi=qp(G,mod-2,mod);
    	for(int i=0;i<m-1;++i) mp[qp(g,i,m)]=i;//模意义取log 
    	for(int i=1;i<=s;++i)
    	{
    		int x; read(x);
    		if(x) ++a[mp[x]];
    	}
    	b[mp[1]]=1;
    	
    	len=1; L=0;
    	while(len < (m<<1)) len<<=1,++L;
    	for(int i=0;i<len;++i) r[i]=((r[i>>1]>>1)|((i&1)<<(L-1)));
    	while(n)
    	{
    		if(n&1) mul(a,b,b);
    		mul(a,a,a);
    		n>>=1;
    	}
    	cout<<(b[mp[x]]%mod+mod)%mod<<endl;
    	return 0;
    }
    
  • 相关阅读:
    init进程解析rc文件的相关函数分析
    Ubuntu 安装Android Studio与使用手册
    Vim的撤销与重做
    Vim 配色设置与配色脚本语法
    js常用的语句
    xshell常用的命令
    java常用的语句
    maven工程配置日志
    根据一个oss的pdf文件的 地址转换成一个File文件
    根据一个oss的地址把图片转换成一个文件
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11968875.html
Copyright © 2011-2022 走看看