【NTT】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中的所有元素。
1<=N<=10^9,3<=M<=8000,M为质数
0<=x<=M-1,输入数据保证集合S中元素不重复x∈[1,m-1]

集合中的数∈[0,m-1]

Output

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


题目分析

用$f_{i,j}$表示选了$i$个数乘积为$j$的方案数,不难得到式子$f_{2i,j}=\sum\limits_{a*b\equiv j(\mod m)}f_{i,a}\times f_{i,b}$

对于乘法转加法,常见套路就是取对数。那么这里在模意义下,注意到原根$g$有很好的性质:$g^0,g^1,\cdots ,g^{m-2}$可以取遍$[1\cdots m-1]$,因此令$j\equiv g^A(\mod m)$,不妨用$A$代替$j$,以此类推。所以剩下的就是一个多项式快速幂的过程了,最终答案就是$f_x$的系数。

中途写错两个地方:为$[1\cdots m-1]$内元素按$g^i$标号时,写成遍历$[0\cdots m-1]$(这里整体下标减一。说到底还是对式子不熟练。);给$x$重标号时候,写成 if (x==tar&&!fl) tar = x, fl = 1; ……

 

 1 #include<bits/stdc++.h>
 2 #define MO 1004535809
 3 const int maxn = 50035;
 4 
 5 int T,n,m,tar,len,dt,inv3,invn,ort;
 6 int f[maxn],g[maxn],cov[maxn],tmp1[maxn],tmp2[maxn];
 7 bool vis[maxn];
 8 
 9 int read()
10 {
11     char ch = getchar();
12     int num = 0, fl = 1;
13     for (; !isdigit(ch); ch=getchar())
14         if (ch=='-') fl = -1;
15     for (; isdigit(ch); ch=getchar())
16         num = (num<<1)+(num<<3)+ch-48;
17     return num*fl;
18 }
19 int qmi(int a, int b, int p)
20 {
21     int ret = 1;
22     for (; b; b>>=1, a=1ll*a*a%p)
23         if (b&1) ret = 1ll*ret*a%p;
24     return ret;
25 }
26 int fndRoot()
27 {
28     int fac[10035], tot = 0, pos = m-1;
29     for (int i=2; i*i<=pos; i++)
30         if (pos%i==0){
31             fac[++tot] = i;
32             while (pos%i==0) pos /= i;
33         }
34     if (pos!=1) fac[++tot] = pos;
35     pos = m-1;
36     for (int i=2; i<=pos; i++)
37     {
38         bool chk = true;
39         for (int j=1; j<=tot&&chk; j++)
40             if (qmi(i, pos/fac[j], m)==1) chk = false;
41         if (chk) return i;
42     }
43     return -1;
44 }
45 void NTT(int *a, int opt)
46 {
47     for (int i=0; i<len; i++)
48         if (i < cov[i]) std::swap(a[i], a[cov[i]]);
49     for (int i=1; i<len; i<<=1)
50     {
51         int Wn = qmi(3, (MO-1)/(i<<1), MO);
52         if (opt==-1) Wn = qmi(inv3, (MO-1)/(i<<1), MO);
53         for (int j=0, p=i<<1; j<len; j+=p)
54         {
55             int w = 1;
56             for (int k=0; k<i; k++, w=1ll*w*Wn%MO)
57             {
58                 int valx = a[j+k], valy = 1ll*w*a[i+j+k]%MO;
59                 a[j+k] = (valx+valy)%MO, a[i+j+k] = (valx-valy+MO)%MO;
60             }
61         }
62     }
63     if (opt==-1) for (int i=0; i<len; i++) a[i] = 1ll*a[i]*invn%MO;
64 }
65 void mult(int *a, int *b)
66 {
67     for (int i=0; i<len; i++) tmp1[i] = a[i], tmp2[i] = b[i];
68     NTT(tmp1, 1), NTT(tmp2, 1);
69     for (int i=0; i<len; i++) tmp1[i] = 1ll*tmp1[i]*tmp2[i]%MO;
70     NTT(tmp1, -1);
71     for (int i=0; i<m-1; i++) a[i] = (tmp1[i]+tmp1[i+m-1])%MO;
72 }
73 int main()
74 {
75     T = read(), m = read(), tar = read(), n = read();
76     inv3 = qmi(3, MO-2, MO), ort = fndRoot();
77     for (int i=1; i<=n; i++) vis[read()] = true;
78     for (int i=0, x=1, fl=0; i<m-1; i++, x=1ll*x*ort%m)
79     {
80         if (vis[x]) g[i] = 1;
81         if (x==tar&&!fl) tar = i, fl = 1;
82     }
83     for (len=1; len<=(m-1)<<1; len<<=1) ++dt;
84     for (int i=0; i<len; i++)
85         cov[i] = (cov[i>>1]>>1)|((i&1)<<(dt-1));
86     f[0] = 1, invn = qmi(len, MO-2, MO);
87     for (; T; T>>=1, mult(g, g))
88         if (T&1) mult(f, g);
89     printf("%d\n",f[tar]);
90     return 0;
91 }

 

 

 

 

END

posted @ 2019-03-18 20:01  AntiQuality  阅读(170)  评论(0编辑  收藏  举报