bzoj3992【SDOI2015】序列统计

3992: [SDOI2015]序列统计

Time Limit: 30 Sec  Memory Limit: 128 MB

Submit: 673  Solved: 327

[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

Round 1 感谢yts1999上传

NTT第一题

首先能够发现1004535809=479*2^21+1,并且是一个质数,所以能够用NTT解决。

用f[i][j]表示i个数模m等于g^j的方案数(i为2的整数次幂,g为m的原根)。则f[i][j]=∑f[i/2][k]*f[i/2][j-k]。

这样就成了卷积形式,NTT搞定。

但n并不一定是2的整数次幂,这里就要用到高速幂的思想(详见代码)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 40000
#define mod 1004535809
using namespace std;
int n,m,num,s,mg,g,bit,inv;
int a[maxn],c[maxn],A[maxn],B[maxn],ind[maxn],rev[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline ll power(ll x,int y,int p)
{
	ll ret=1;
	for(;y;y>>=1,x=x*x%p) if (y&1) ret=ret*x%p;
	return ret;
}
inline bool get_order(int x,int m)
{
	int lim=sqrt(m),phi=m-1;
	F(i,1,lim) if (phi%i==0)
	{
		if (power(x,i,m)==1){if (i!=m-1) return false;}
		if (power(x,phi/i,m)==1){if (phi/i!=m-1) return false;}
	}
	return true;
}
inline int get_primitive_root(int x)
{
	F(i,2,x) if (get_order(i,x)) return i;
}
inline void ntt(int *a,int flag)
{
	F(i,0,(1<<bit)-1) if (rev[i]>i) swap(a[i],a[rev[i]]);
	F(i,1,bit)
	{
		int y=(1ll*flag*(mod-1)/(1<<i)+mod-1)%(mod-1);
		ll wn=power(g,y,mod);
		for(int j=0;(j<1<<bit);j+=(1<<i))
		{
			ll w=1;
			F(k,j,j+(1<<(i-1))-1)
			{
				int u=a[k],v=w*a[k+(1<<(i-1))]%mod;
				a[k]=(u+v)%mod;
				a[k+(1<<(i-1))]=((u-v)%mod+mod)%mod;
				w=w*wn%mod;
			}
		}
	}
	if (flag<0) F(i,0,(1<<bit)-1) a[i]=1ll*a[i]*inv%mod;
}
inline void convol(int *a,int *b)
{
	int len=1<<bit;
	F(i,0,len-1) c[i]=b[i];
	ntt(a,1);ntt(c,1);
	F(i,0,len-1) a[i]=(ll)a[i]*c[i]%mod;
	ntt(a,-1);
	for(int i=m-1,j=0;i<len;i++,j++) a[j]=(a[j]+a[i])%mod,a[i]=0;
}
int main()
{
	n=read();m=read();num=read();s=read();
	F(i,1,s) a[i]=read();
	mg=get_primitive_root(m);
	g=get_primitive_root(mod);
	int tmp=1;
	F(i,0,m-2){ind[tmp]=i;tmp=tmp*mg%m;}
	for(bit=0;(1<<bit)<(m-1)*2;bit++);
	inv=power(1<<bit,mod-2,mod);
	F(i,0,(1<<bit)-1) rev[i]=rev[i>>1]>>1|((i&1)<<(bit-1));
	A[0]=1;
	F(i,1,s) if (a[i]) B[ind[a[i]]]=1;
	for(;n;convol(B,B),n>>=1) if (n&1) convol(A,B);
	printf("%d\n",A[ind[num]]);
	return 0;
}
时间: 2024-10-07 11:15:25

bzoj3992【SDOI2015】序列统计的相关文章

[bzoj3992][SDOI2015]序列统计——离散对数+NTT

题目大意: 给定一个数字不超过\(m\)的集合\(S\),用\(S\)中的数生成一个长度为\(n\)的序列,求所有序列中的元素乘积模\(m\)等于\(x\)的序列的个数. 思路: 考虑最朴素的\(DP\),设\(f_{i,j}\)为选了\(i\)个数,乘积模\(m\)余\(j\)的方案数,直接转移的时间复杂度是\(O(nm^2)\)的. 不难发现每次转移的过程是相同的,矩阵加速显然不太可行,考虑将乘法形式的转移变成加法形式的转移,这样每次转移即可用NTT优化. 这里需要用到一个叫做离散对数的东西

bzoj3992 [SDOI2015]序列统计

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

【动态规划】bzoj3992 [Sdoi2015]序列统计 10分

#include<cstdio> using namespace std; #define MOD 1004535809 int a[8001],f[1001][101],n,m,x,S; int main() { scanf("%d%d%d%d",&n,&m,&x,&S); for(int i=1;i<=S;++i) { scanf("%d",&a[i]); a[i]%=m; ++f[1][a[i]]; }

BZOJ3992 [SDOI2015]序列统计 【生成函数 + 多项式快速幂】

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

【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂

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

BZOJ 3992: [SDOI2015]序列统计 NTT+快速幂

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1155  Solved: 532[Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中

BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]

3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 466[Submit][Status][Discuss] Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中

BZOJ 3992: [SDOI2015]序列统计 快速幂+NTT(离散对数下)

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

Bzoj3992:[SDOI2015]序列统计

题面 Bzoj Sol pts 1 大暴力很简单,\(f[i][j]\)表示到第\(i\)个位置,前面积的模为\(j\)的方案 然后可以获得\(10\)分的好成绩 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) using namespace std; typedef long long ll; const int Zsy(

BZOJ3992:[SDOI2015]序列统计——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=3992 https://www.luogu.org/problemnew/show/P3321 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个