您的位置:首页 > 其它

BZOJ 3992 [SDOI 2015] 序列统计 解题报告

2015-04-16 09:11 337 查看
这个题最暴力的搞法就是这样的:

设 $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$ 的数。。。

具体细节自己脑补脑补吧~

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
#define N 16384 + 5
#define Mod 1004535809
#define g 3

int n, p, x, m, len, w, d, root, inv_len, ans;
int T
, Num
, Pos
;
int A
, B
, C
, Rev
, e[2]
;

inline int power(int u, int v, int p)
{
int res = 1;
for (; v; v >>= 1)
{
if (v & 1) res = (LL) res * u % p;
u = (LL) u * u % p;
}
return res;
}

inline void Init()
{
scanf("%d%d%d%d", &n, &p, &x, &m);
for (int i = 1; i <= m; i ++)
scanf("%d", T + i);
for (len = p << 1; len != (len & -len); len += (len & -len)) ;
for (int i = len; i > 1; i >>= 1) d ++;
inv_len = power(len, Mod - 2, Mod);
w = power(g, (Mod - 1) / len, Mod);
}

inline bool Judge(int x, int p)
{
for (int i = 2; i * i <= p; i ++)
if ((p - 1) % i == 0 && power(x, (p - 1) / i, p) == 1) return 0;
return 1;
}

inline int Find_Root(int p)
{
if (p == 2) return 1;
int res = 2;
for (; !Judge(res, p); res ++) ;
return res;
}

inline void Prepare()
{
root = Find_Root(p);
for (int i = 0; i < p - 1; i ++)
{
Num[i] = !i ? 1 : Num[i - 1] * root % p;
Pos[Num[i]] = i;
}
}

inline int Inc(int u, int v)
{
return u + v - (u + v >= Mod ? Mod : 0);
}

inline void FFT(int *Ar, int op)
{
for (int i = 0; i < len; i ++)
if (Rev[i] > i) swap(Ar[i], Ar[Rev[i]]);
for (int k = 1, s = 1; k < len; k <<= 1, s ++)
for (int i = 0; i < len; i ++)
{
if (i & k) continue ;
int t = (i & k - 1) << d - s;
int u = Inc(Ar[i], (LL) Ar[i + k] * e[op][t] % Mod);
int v = Inc(Ar[i], Mod - ((LL) Ar[i + k] * e[op][t] % Mod));
Ar[i] = u, Ar[i + k] = v;
}
}

inline void Convol(int *U, int *V)
{
for (int i = 0; i < len; i ++)
C[i] = V[i];
FFT(U, 0), FFT(C, 0);
for (int i = 0; i < len; i ++)
U[i] = (LL) U[i] * C[i] % Mod;
FFT(U, 1);
for (int i = 0; i < len; i ++)
U[i] = (LL) U[i] * inv_len % Mod;
for (int i = len - 1; i >= p - 1; i --)
{
U[i - p + 1] = Inc(U[i - p + 1], U[i]);
U[i] = 0;
}
}

inline void Solve()
{
A[0] = 1;
for (int i = 1; i <= m; i ++)
{
if (T[i] == 0) continue ;
B[Pos[T[i]]] ++;
}
for (int i = 0, inv_w = power(w, Mod - 2, Mod); i < len; i ++)
{
e[0][i] = !i ? 1 : (LL) e[0][i - 1] * w % Mod;
e[1][i] = !i ? 1 : (LL) e[1][i - 1] * inv_w % Mod;
for (int j = 0; j < d; j ++)
if ((i >> j) & 1) Rev[i] += 1 << (d - j - 1);
}
for (; n; n >>= 1)
{
if (n & 1) Convol(A, B);
Convol(B, B);
}
ans = A[Pos[x]];
}

int main()
{
#ifndef ONLINE_JUDGE
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
#endif

Init();
Prepare();
Solve();
printf("%d\n", ans);

#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}


3992_Gromah
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: