Shell Necklace(CDQ分治+FFT)

描述

传送门:2016 Multi-University Training Contest 1 - Problem H

Perhaps the sea‘s definition of a shell is the pearl. However, in my view, a shell necklace with n beautiful shells contains the most sincere feeling for my best lover Arrietty, but even that is not enough.

Suppose the shell necklace is a sequence of shells (not a chain end to end). Considering i continuous shells in the shell necklace, I know that there exist different schemes to decorate the i shells together with one declaration of love.

I want to decorate all the shells with some declarations of love and decorate each shell just one time. As a problem, I want to know the total number of schemes.

Input

There are multiple test cases(no more than $20$ cases and no more than $1$ in extreme case), ended by $0$.

For each test cases, the first line contains an integer $n$, meaning the number of shells in this shell necklace, where $1≤n≤10^5$. Following line is a sequence with $n$ non-negative integer $a_1,a_2,…,a_n$, and $a_i≤10^7$ meaning the number of schemes to decorate $i$ continuous shells together with a declaration of love.

Output

For each test case, print one line containing the total number of schemes module $313$(Three hundred and thirteen implies the march 13th, a special and purposeful day).

Sample Input

1
2
3
4
5
3
1 3 7
4
2 2 2 2
0

Sample Output

1
2
14
54

Hint

Hint
For the first test case in Sample Input, the Figure 1 provides all schemes about it. The total number of schemes is $1 + 3 + 3 + 7 = 14$.

思路

  • $dp[i]$表示串成长度为$i$的方案数,$dp[0] = 1$。那么转移方程$$dp[i] = \sum_{j = 1} ^ i a[j]dp[i - j]$$
  • 这种卷积形式可以使用CDQ分治和FFT求解。
  • 假设cdq(l, r)求解$dp[l], dp[l + 1], …, dp[r]$的值。如果已经通过cdq(l, m)求出了左半部分的答案,考虑其对$dp[l], dp[l + 1], …, dp[r]$的贡献。令$g[i]$表示$dp[l], dp[l + 1], …, dp[m]$对$dp[i]$的贡献,那么有$$g[i] = \sum_{j = l}^m dp[j] a[i - j]$$
  • 令$x[i] = dp[i + l]$ ,$y[i] = a[i + 1]$,则有
    $$
    \begin{align*}
    g[i] &= \sum_{j = l} ^ m dp[j] a[i - j] = \sum_{j = l} ^ m x[j - l] y[i - j - 1] \\
    &= \sum_{j = l} ^ {i - 1} x[j - l] y[i - j - 1] \\
    &= \sum_{j = 0} ^ {i - l - 1} x[j] y[i - l - 1 - j] = z[i - l - 1]
    \end{align*}
    $$
  • $x$序列和$y$序列做一下FFT就好了。
  • 不理解的话,可以在草稿纸上单步跑一下……

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <bits/stdc++.h>
using namespace std;

namespace fft
{
typedef double db;

struct cp
{
db x, y;
cp() { x = y = 0; }
cp(db x, db y) : x(x), y(y) {}
};

inline cp operator+(cp a, cp b) { return cp(a.x + b.x, a.y + b.y); }
inline cp operator-(cp a, cp b) { return cp(a.x - b.x, a.y - b.y); }
inline cp operator*(cp a, cp b) { return cp(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); }
inline cp conj(cp a) { return cp(a.x, -a.y); }

int base = 1;
vector<cp> roots = {{0, 0}, {1, 0}};
vector<int> rev = {0, 1};

const db PI = acosl(-1.0);

void ensure_base(int nbase)
{
if (nbase <= base) return;
rev.resize(static_cast<unsigned long>(1 << nbase));
for (int i = 0; i < (1 << nbase); i++)
rev[i] = (rev[i >> 1] >> 1) + ((i & 1) << (nbase - 1));
roots.resize(static_cast<unsigned long>(1 << nbase));
while (base < nbase)
{
db angle = 2 * PI / (1 << (base + 1));
for (int i = 1 << (base - 1); i < (1 << base); i++)
{
roots[i << 1] = roots[i];
db angle_i = angle * (2 * i + 1 - (1 << base));
roots[(i << 1) + 1] = cp(cos(angle_i), sin(angle_i));
}
base++;
}
}

void fft(vector<cp>& a, int n = -1)
{
if (n == -1) n = a.size();
assert((n & (n - 1)) == 0);
int zeros = __builtin_ctz(n);
ensure_base(zeros);
int shift = base - zeros;
for (int i = 0; i < n; i++)
if (i < (rev[i] >> shift))
swap(a[i], a[rev[i] >> shift]);
for (int k = 1; k < n; k <<= 1)
for (int i = 0; i < n; i += 2 * k)
for (int j = 0; j < k; j++)
{
cp z = a[i + j + k] * roots[j + k];
a[i + j + k] = a[i + j] - z;
a[i + j] = a[i + j] + z;
}
}

vector<cp> fa, fb;

template <class T>
vector<T> multiply(vector<T>& a, vector<T>& b)
{
int need = a.size() + b.size() - 1;
int nbase = 0;
while ((1 << nbase) < need) nbase++;
ensure_base(nbase);
int sz = 1 << nbase;
if (sz > (int)fa.size())
fa.resize(static_cast<unsigned long>(sz));
for (int i = 0; i < sz; i++)
{
T x = (i < (int)a.size() ? a[i] : 0);
T y = (i < (int)b.size() ? b[i] : 0);
fa[i] = cp(x, y);
}
fft(fa, sz);
cp r(0, -0.25 / sz);
for (int i = 0; i <= (sz >> 1); i++)
{
int j = (sz - i) & (sz - 1);
cp z = (fa[j] * fa[j] - conj(fa[i] * fa[i])) * r;
if (i != j)
{
fa[j] = (fa[i] * fa[i] - conj(fa[j] * fa[j])) * r;
}
fa[i] = z;
}
fft(fa, sz);
vector<T> res(static_cast<unsigned long>(need));
for (int i = 0; i < need; i++) res[i] = fa[i].x + 0.5;
return res;
}
}; // namespace fft

const int mod = 313;

int main()
{
int n;
while (~scanf("%d", &n) && n)
{
vector<int> a(n + 1);
vector<int> dp(n + 1);
dp[0] = 1;
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]), a[i] %= mod;
function<void(int, int)> cdq = [&](int l, int r) {
if (l == r) return;
const int m = l + r >> 1;
cdq(l, m);
vector<int> x(m - l + 1), y(r - l);
for (int i = l; i <= m; i++) x[i - l] = dp[i];
for (int i = 0; i < r - l; i++) y[i] = a[i + 1];
auto ret = fft::multiply(x, y);
for (int i = m + 1; i <= r; i++) (dp[i] += ret[i - l - 1]) %= mod;
cdq(m + 1, r);
};
cdq(0, n);
printf("%lld\n", dp[n]);
}
}
捐助作者
0%