bzoj3992 [SDOI2015]序列统计

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中元素不重复

正解:$DP$+$NTT$。

首先考虑暴力$DP$,$f[i][j]$表示统计到第$i$位,乘积为$j$的方案数,然后很显然,$f[i][(k*p)\mod m]=f[i-1][k]*sum[p]$,其中$sum[p]$为p这个数出现的次数。

但是这样是肯定会$T$飞的,所以我们考虑优化。话说这个优化还是蛮玄学的。。

我们可以把乘法变成加法,然后两个数相乘就是它们的对数相加,当然底数要相同,并且对数肯定要是整数。

因为$m$是质数,所以$m$必定有原根。我们又知道,原根的$[1,m-1]$次方对应了$[1,m-1]$这些数,所以我们可以求出$[1,m-1]$的离散对数,也就是使得原根$g^{x}=i$的$x$(然而我没学过所以不会。。)

设$ind[i]$为$i$在模$m$意义下的离散对数,于是$f[i][(ind[k]+ind[p])\mod (m-1)]=f[i-1][ind[k]]*sum[ind[p]]$(注意这里第二维的取值范围是$m-1$,所以还要加一个特判)

因为答案对$1004535809$取模,所以显然这个方程可以用$NTT$优化,但是$n$的范围很大,有$10^{9}$级别,不过我们在卷积外面套一个快速幂就行了。

我们先用快速幂算出$sum$的$n$次方,然后再用$f[0]*sum$,最后输出$f[n][ind[x]]$,就是我们所要的答案。

  1 //It is made by wfj_2048~
  2 #include <algorithm>
  3 #include <iostream>
  4 #include <complex>
  5 #include <cstring>
  6 #include <cstdlib>
  7 #include <cstdio>
  8 #include <vector>
  9 #include <cmath>
 10 #include <queue>
 11 #include <stack>
 12 #include <map>
 13 #include <set>
 14 #define rhl (1004535809)
 15 #define inf (1<<30)
 16 #define NN (100010)
 17 #define G (3)
 18 #define il inline
 19 #define RG register
 20 #define ll long long
 21 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
 22
 23 using namespace std;
 24
 25 int rev[NN],ind[NN],N,n,m,x,s,lg,gg;
 26 ll ans[NN],a[NN],b[NN],sum[NN];
 27
 28 il int gi(){
 29     RG int x=0,q=1; RG char ch=getchar();
 30     while ((ch<‘0‘ || ch>‘9‘) && ch!=‘-‘) ch=getchar();
 31     if (ch==‘-‘) q=-1,ch=getchar();
 32     while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-48,ch=getchar();
 33     return q*x;
 34 }
 35
 36 il ll qpow(RG ll a,RG ll b){
 37     RG ll ans=1;
 38     while (b){
 39     if (b&1) (ans*=a)%=rhl;
 40     (a*=a)%=rhl,b>>=1;
 41     }
 42     return ans;
 43 }
 44
 45 il void NTT(ll *a,RG int n,RG int f){
 46     for (RG int i=0;i<n;++i) if (i<rev[i]) swap(a[i],a[rev[i]]);
 47     for (RG int i=1;i<n;i<<=1){
 48     RG ll gn=qpow(G,(rhl-1)/(i<<1)),x,y;
 49     for (RG int j=0;j<n;j+=(i<<1)){
 50         RG ll g=1;
 51         for (RG int k=0;k<i;++k,(g*=gn)%=rhl){
 52         x=a[j+k],y=g*a[j+k+i]%rhl;
 53         a[j+k]=(x+y)%rhl,a[j+k+i]=(x-y+rhl)%rhl;
 54         }
 55     }
 56     }
 57     if (f==1) return; reverse(a+1,a+n); RG ll inv=qpow(n,rhl-2);
 58     for (RG int i=0;i<n;++i) (a[i]*=inv)%=rhl;
 59     for (RG int i=m;i<n;++i) (a[i%(m-1) ? i%(m-1) : m-1]+=a[i])%=rhl,a[i]=0; return;
 60 }
 61
 62 il void NTTpow(ll *a,RG int b){
 63     for (RG int i=0;i<N;++i) ans[i]=a[i]; b--;
 64     while (b){
 65     NTT(a,N,1);
 66     if (b&1){
 67         NTT(ans,N,1);
 68         for (RG int i=0;i<N;++i) (ans[i]*=a[i])%=rhl;
 69         NTT(ans,N,-1);
 70     }
 71     for (RG int i=0;i<N;++i) (a[i]*=a[i])%=rhl;
 72     NTT(a,N,-1); b>>=1;
 73     }
 74     for (RG int i=0;i<N;++i) a[i]=ans[i]; return;
 75 }
 76
 77 il int isroot(RG int x){
 78     RG int l=1;
 79     for (RG int i=1;i<m-1;++i){
 80     (l*=x)%=m;
 81     if (l==1) return 0;
 82     }
 83     return 1;
 84 }
 85
 86 il void pre(){
 87     for (RG int i=2;i<m;++i) if (isroot(i)){ gg=i; break; }
 88     RG int l=1; for (RG int i=1;i<m;++i) (l*=gg)%=m,ind[l]=i; return;
 89 }
 90
 91 il void work(){
 92     n=gi(),m=gi(),x=gi(),s=gi(); pre();
 93     for (RG int i=1,v;i<=s;++i) sum[ind[v=gi()]]++;
 94     for (N=1;N<=(m<<1);N<<=1) lg++; a[ind[1]]=1,sum[0]=0;
 95     for (RG int i=0;i<N;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
 96     NTTpow(sum,n); NTT(a,N,1),NTT(sum,N,1);
 97     for (RG int i=0;i<N;++i) (a[i]*=sum[i])%=rhl;
 98     NTT(a,N,-1); printf("%lld",a[ind[x]]); return;
 99 }
100
101 int main(){
102     File("sequence");
103     work();
104     return 0;
105 }
时间: 2024-08-07 19:24:58

bzoj3992 [SDOI2015]序列统计的相关文章

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

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

【动态规划】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的不同的数列的有多少个