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

    [SDOI2015]序列统计

    题目描述

    小C有一个集合(S),里面的元素都是小于(M)的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为(N)的数列,数列中的每个数都属于集合(S)。小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数(x),求所有可以生成出的,且满足数列中所有数的乘积(mod M)的值等于(x)的不同的数列的有多少个。小C认为,两个数列({A_i})({B_i})不同,当且仅当至少存在一个整数i,满足(A_i≠B_i)。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案(mod 1004535809)的值就可以了。

    输入输出格式

    输入格式:

    一行,四个整数,(N、M、x、|S|),其中(|S|)为集合S中元素个数。第二行,(|S|)个整数,表示集合(S)中的所有元素。

    输出格式:

    一行,一个整数,表示你求出的种类数(mod 1004535809)的值。

    输入输出样例

    输入样例#1:

    复制

    4 3 1 2
    1 2
    

    输出样例#1:

    复制

    8
    

    说明

    【样例说明】

    可以生成的满足要求的不同的数列有(1,1,1,1)、(1,1,2,2)、(1,2,1,2)、(1,2,2,1)、(2,1,1,2)、(2,1,2,1)、(2,2,1,1)、(2,2,2,2)。

    【数据规模和约定】

    对于10%的数据,(1le Nle 1000)

    对于30%的数据,(3le Mle 100)

    对于60%的数据,(3le Mle 800)

    对于全部的数据,(1le Nle 10^9,3le Mle 8000,M为质数,1le xle M-1),输入数据保证集合(S)中元素不重复

    我们构造函数(A(x)=sum_{i}[iin S]x^i),答案就是(A)自卷(N)次过后第(x)项的系数。

    不过这个卷积不太寻常:(displaystyle c(x)=sum_{i=1}^Msum_{j=1}^M[i*j\% M==x]cdot a(i)cdot b(j))

    除法非常难处理,于是我们考虑转化为我们熟悉的加法。将乘法转化为加法,很容易就想到质数函数。我们设幂为(g),则(g^{a*b}=g^a+g^b)。我们对于没个数(i),我们取(x)为它的代表元,满足(g^x\%M==i)

    考虑选取原根(g),因为(g^x)(xin[1,M-1])时两两不同。

    于是我们将0舍去,然后就可以将乘法卷积转化为加法卷积了。具体实现要用到快速幂,每次卷积一次后,要将下标(xge M-1)的部分的值累加在(x-(M-1))下标的位置上。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define MAXM 8005
    #define mod 1004535809
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m,x,num;
    ll ksm(ll t,ll x,ll M) {
    	ll ans=1;
    	for(;x;x>>=1,t=t*t%M)
    		if(x&1) ans=ans*t%M;
    	return ans;
    }
    
    ll Find(ll m) {
    	if(m==2) return 1;
    	for(int i=2;i<=m-1;i++) {
    		int flag=1;
    		for(int j=2;j*j<m;j++) {
    			if(ksm(i,(m-1)/j,m)==1) {
    				flag=0;
    				break;
    			}
    		}
    		if(flag==1) return i;
    	}
    }
    
    int id[MAXM];
    int rev[MAXM<<2];
    void NTT(ll *a,int d,int flag) {
    	static const ll G=3;
    	int n=1<<d;
    	for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
    	for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    	for(int s=1;s<=d;s++) {
    		int len=1<<s,mid=len>>1;
    		ll w=flag==1?ksm(G,(mod-1)/len,mod):ksm(G,mod-1-(mod-1)/len,mod);
    		for(int i=0;i<n;i+=len) {
    			ll t=1;
    			for(int j=0;j<mid;j++,t=t*w%mod) {
    				ll u=a[i+j];
    				ll v=t*a[i+j+mid]%mod;
    				a[i+j]=(u+v)%mod;
    				a[i+j+mid]=(u-v+mod)%mod;
    			}
    		}
    	}
    	if(flag==-1) {
    		ll inv=ksm(n,mod-2,mod);
    		for(int i=0;i<n;i++) a[i]=a[i]*inv%mod;
    	}
    }
    
    ll a[MAXM<<2];
    ll f[MAXM<<2],ans[MAXM<<2];
    void mul(ll *a,ll *b,int d) {
    	for(int i=0;i<(1<<d);i++) a[i]=a[i]*b[i]%mod;
    	NTT(a,d,-1);
    	for(int i=0;i<m-1;i++) (a[i]+=a[i+m-1])%=mod,a[i+m-1]=0;
    }
    
    void ksm(int k) {
    	ans[0]=1;
    	int d=ceil(log2(m*2));
    	for(;k;k>>=1) {
    		if(k&1) {
    			NTT(ans,d,1),NTT(f,d,1);
    			mul(ans,f,d);
    			NTT(f,d,-1);
    		}
    		NTT(f,d,1);
    		mul(f,f,d);
    	}
    }
    
    int main() {
    	n=Get(),m=Get(),x=Get(),num=Get();
    	ll g=Find(m);
    	ll now=1;
    	for(int i=0;i<m-1;i++) {
    		id[now]=i;
    		now=now*g%m;
    	}
    	int a;
    	for(int i=1;i<=num;i++) {
    		a=Get();
    		if(a) f[id[a]]=1;
    	}
    	ksm(n);
    	cout<<ans[id[x]];
    	return 0;
    }
    
    
  • 相关阅读:
    适用于实数范围的中缀表达式的 +
    Django官方文档学习2——数据库及模板
    github命令
    千行代码入门Python
    Notepad++配置Python运行环境
    Python常用网页字符串处理技巧
    requests设置headers,proxies,cookies
    Django官方文档学习1——第一个helloworld页面
    笔记本键盘上没有break键的解决方案
    Python beautifulsoup模块
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10048336.html
Copyright © 2011-2022 走看看