BZOJ 3992 [SDOI 2015] 序列统计 解题报告
这个题最暴力的搞法就是这样的:
设 $Dp[i][j]$ 为前 $i$ 个数乘积为 $j$ 的方案数。
转移的话就不多说了哈。。。
当前复杂度 $O(nm^2)$
注意到,$M$ 是个质数,就说明 $M$ 有原根并且我们可以很快的求出来。
于是对于 $1\rightarrow M-1$ 中的每一个数都可以表示成原根的某次幂。
于是乘法可以转化为原根的幂的加法,
转移的时候就相当于做多项式乘法了。
我们再注意到,$1004535809 = 479 \times 2^{21} + 1$ 并且是个质数,原根为 $3$。
于是转移的时候就可以用 $FFT$ 优化了。
当前复杂度 $O(nm\log m)$
我们再考虑,每次多项式乘法中乘的多项式都是一样的,那么是不是就可以快速幂啊?
当前复杂度 $O(m\log m\log n)$,可以 A 掉这个题啦~
注意那些等于 $0$ 的数。。。
具体细节自己脑补脑补吧~
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 typedef long long LL; 8 #define N 16384 + 5 9 #define Mod 1004535809 10 #define g 3 11 12 int n, p, x, m, len, w, d, root, inv_len, ans; 13 int T[N], Num[N], Pos[N]; 14 int A[N], B[N], C[N], Rev[N], e[2][N]; 15 16 inline int power(int u, int v, int p) 17 { 18 int res = 1; 19 for (; v; v >>= 1) 20 { 21 if (v & 1) res = (LL) res * u % p; 22 u = (LL) u * u % p; 23 } 24 return res; 25 } 26 27 inline void Init() 28 { 29 scanf("%d%d%d%d", &n, &p, &x, &m); 30 for (int i = 1; i <= m; i ++) 31 scanf("%d", T + i); 32 for (len = p << 1; len != (len & -len); len += (len & -len)) ; 33 for (int i = len; i > 1; i >>= 1) d ++; 34 inv_len = power(len, Mod - 2, Mod); 35 w = power(g, (Mod - 1) / len, Mod); 36 } 37 38 inline bool Judge(int x, int p) 39 { 40 for (int i = 2; i * i <= p; i ++) 41 if ((p - 1) % i == 0 && power(x, (p - 1) / i, p) == 1) return 0; 42 return 1; 43 } 44 45 inline int Find_Root(int p) 46 { 47 if (p == 2) return 1; 48 int res = 2; 49 for (; !Judge(res, p); res ++) ; 50 return res; 51 } 52 53 inline void Prepare() 54 { 55 root = Find_Root(p); 56 for (int i = 0; i < p - 1; i ++) 57 { 58 Num[i] = !i ? 1 : Num[i - 1] * root % p; 59 Pos[Num[i]] = i; 60 } 61 } 62 63 inline int Inc(int u, int v) 64 { 65 return u + v - (u + v >= Mod ? Mod : 0); 66 } 67 68 inline void FFT(int *Ar, int op) 69 { 70 for (int i = 0; i < len; i ++) 71 if (Rev[i] > i) swap(Ar[i], Ar[Rev[i]]); 72 for (int k = 1, s = 1; k < len; k <<= 1, s ++) 73 for (int i = 0; i < len; i ++) 74 { 75 if (i & k) continue ; 76 int t = (i & k - 1) << d - s; 77 int u = Inc(Ar[i], (LL) Ar[i + k] * e[op][t] % Mod); 78 int v = Inc(Ar[i], Mod - ((LL) Ar[i + k] * e[op][t] % Mod)); 79 Ar[i] = u, Ar[i + k] = v; 80 } 81 } 82 83 inline void Convol(int *U, int *V) 84 { 85 for (int i = 0; i < len; i ++) 86 C[i] = V[i]; 87 FFT(U, 0), FFT(C, 0); 88 for (int i = 0; i < len; i ++) 89 U[i] = (LL) U[i] * C[i] % Mod; 90 FFT(U, 1); 91 for (int i = 0; i < len; i ++) 92 U[i] = (LL) U[i] * inv_len % Mod; 93 for (int i = len - 1; i >= p - 1; i --) 94 { 95 U[i - p + 1] = Inc(U[i - p + 1], U[i]); 96 U[i] = 0; 97 } 98 } 99 100 inline void Solve() 101 { 102 A[0] = 1; 103 for (int i = 1; i <= m; i ++) 104 { 105 if (T[i] == 0) continue ; 106 B[Pos[T[i]]] ++; 107 } 108 for (int i = 0, inv_w = power(w, Mod - 2, Mod); i < len; i ++) 109 { 110 e[0][i] = !i ? 1 : (LL) e[0][i - 1] * w % Mod; 111 e[1][i] = !i ? 1 : (LL) e[1][i - 1] * inv_w % Mod; 112 for (int j = 0; j < d; j ++) 113 if ((i >> j) & 1) Rev[i] += 1 << (d - j - 1); 114 } 115 for (; n; n >>= 1) 116 { 117 if (n & 1) Convol(A, B); 118 Convol(B, B); 119 } 120 ans = A[Pos[x]]; 121 } 122 123 int main() 124 { 125 #ifndef ONLINE_JUDGE 126 freopen("sequence.in", "r", stdin); 127 freopen("sequence.out", "w", stdout); 128 #endif 129 130 Init(); 131 Prepare(); 132 Solve(); 133 printf("%d\n", ans); 134 135 #ifndef ONLINE_JUDGE 136 fclose(stdin); 137 fclose(stdout); 138 #endif 139 return 0; 140 }