zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    第 425 期 Python 周刊
    第 423 期 Python 周刊
    第423期 Python 周刊
    Python Weekly 422
    第421期 Python 周刊
    第420期 Python 周刊
    LeetCode 3: 无重复字符的最长子串 Longest Substring Without Repeating Characters
    Python Weekly 419
    LeetCode 771: 宝石与石头 Jewels and Stones
    LeetCode 652: 寻找重复的子树 Find Duplicate Subtrees
  • 原文地址:https://www.cnblogs.com/wfj2048/p/6671026.html
Copyright © 2011-2022 走看看