BZOJ 3992: [SDOI2015]序列统计

3992: [SDOI2015]序列统计

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 1147  Solved: 527
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

4 3 1 2
1 2

Sample Output

8

HINT

【样例说明】

可以生成的满足要求的不同的数列有(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%的数据,1<=N<=1000;

对于30%的数据,3<=M<=100;

对于60%的数据,3<=M<=800;

对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复

Source

分析:

首先考虑最直接的算法$DP$...$f[i][j]$代表前$i$位乘积为$j$的方案数,但是$n$是$1o^9$...感觉这个数据范围告诉我们“我叫快速幂”...

考虑原根$p$的定义,所以我们可以把每个数字用$p$的不同次幂表示,这就把乘法变成了加法...貌似可以用$NTT$来优化...

所以我们就得到了一个优秀的$O(MlogMlogN)$的算法...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=20000+5,mod=1004535809,G=3;

int n,k,m,N,L,t,sum,tim,root,s[maxn],R[maxn],a[maxn],b[maxn],vis[maxn],num[maxn];

inline int power(int x,int y){
	int res=1;
	while(y){
		if(y&1) 
			res=1LL*res*x%mod;
		x=1LL*x*x%mod,y>>=1;
	}
	return res;
}

inline void NTT(int *a,int f){
	for(int i=0;i<N;i++)
		if(i<R[i]) swap(a[i],a[R[i]]);
	for(int i=1;i<N;i<<=1){
		int wn=power(G,(mod-1)/(i<<1));
		if(f==-1) wn=power(wn,mod-2);
		for(int j=0;j<N;j+=(i<<1)){
			int w=1;
			for(int k=0;k<i;k++,w=1LL*w*wn%mod){
				int x=a[j+k],y=1LL*w*a[j+k+i]%mod;
				a[j+k]=(x+y)%mod;
				a[j+k+i]=((x-y)%mod+mod)%mod;
			}
		}
	}
	if(f==-1){
		int tmp=power(N,mod-2);
		for(int i=0;i<N;i++)
			a[i]=1LL*a[i]*tmp%mod;
	}
}

inline bool check(int x){
	int no=x;tim++;
	for(int i=1;i<n;i++,no=1LL*no*x%n){
		if(vis[no]==tim) return false;
		vis[no]=tim;
	}
	return true;
}

inline int findroot(void){
	for(int i=2;i<n;i++)
		if(check(i)) return i;
}

inline void power(int y){
	memset(b,0,sizeof(b));b[0]=1;
	while(y){
		NTT(a,1);
		if(y&1){
			NTT(b,1);
			for(int i=0;i<N;i++) b[i]=1LL*a[i]*b[i]%mod;
			NTT(b,-1);
			for(int i=N-1;i>=n-1;i--) b[i-n+1]=(b[i-n+1]+b[i])%mod,b[i]=0;
		}
		for(int i=0;i<N;i++) a[i]=1LL*a[i]*a[i]%mod;NTT(a,-1);
		for(int i=N-1;i>=n-1;i--) a[i-n+1]=(a[i-n+1]+a[i])%mod,a[i]=0;
		y>>=1;
	}
}

signed main(void){
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
#endif
	scanf("%d%d%d%d",&k,&n,&sum,&m);
	for(int i=1;i<=m;i++) scanf("%d",&s[i]);
	t=n<<1;for(N=1;N<=t;N<<=1) L++;
	for(int i=0;i<N;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
	root=findroot();int tmp=1;memset(a,0,sizeof(a));
	for(int i=0;i<n-1;i++) num[tmp]=i,tmp=1LL*tmp*root%n;
	for(int i=1;i<=m;i++) if(s[i]) a[num[s[i]]]++;power(k);
	printf("%d\n",b[num[sum]]);
	return 0;
}

  


By NeighThorn

 

posted @ 2017-03-20 15:06  NeighThorn  阅读(167)  评论(0编辑  收藏  举报