题意
有 n n n种链子,第 i i i种链子长度为 i i i,有 a i a_i ai条,求用这些链子组合出长度为n的链子的方案数。
题解
开始居然没看出来这是一道dp题
令
f
i
f_i
fi为凑出长度为
i
i
i的链子的方案数
则有;
f
i
=
∑
j
=
0
i
−
1
f
j
∗
a
i
−
j
,
f
0
=
1
f_i=\sum_{j=0}^{i-1}f_j*a_{i-j}, f_0=1
fi=j=0∑i−1fj∗ai−j,f0=1
时间复杂度为
O
(
n
2
)
O(n^2)
O(n2),显然要T。
考虑优化。发现右半部分与卷积(
a
∗
b
=
∑
i
=
1
n
a
i
∗
b
i
−
j
a*b=\sum_{i=1}^{n}a_i*b_{i-j}
a∗b=∑i=1nai∗bi−j)类似。所以考虑使用FFT
然而如果说每次转移暴力FFT,则时间复杂度瞬间飙至
O
(
n
2
l
o
g
2
n
)
O(n^2log_2n)
O(n2log2n),不如不优化。
于是我们介绍一个新科技:CDQ分治。
下面介绍一下它的基本步骤:
对 c d q ( l , r ) cdq(l, r) cdq(l,r)
1、求解 c d q ( l , m i d ) , m i d = ⌊ l + r 2 ⌋ cdq(l, mid), mid = \lfloor\frac{l+r}{2}\rfloor cdq(l,mid),mid=⌊2l+r⌋
2、计算 [ l , m i d ] [l,mid] [l,mid]对 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]的贡献(对 [ m i d + 1 , r ] [mid+1, r] [mid+1,r]答案的影响)
3、求解 c d q ( m i d + 1 , r ) cdq(mid+1, r) cdq(mid+1,r)
继续yy优化。我们用
c
d
q
(
l
,
r
)
cdq(l,r)
cdq(l,r)表示求解
f
l
,
f
l
+
1
,
f
l
+
2
.
.
.
f
r
f_l, f_{l+1},f_{l+2}...f_r
fl,fl+1,fl+2...fr
假设求出
c
d
q
(
l
,
m
i
d
)
cdq(l,mid)
cdq(l,mid),令
g
i
g_i
gi为
[
l
,
m
i
d
]
[l, mid]
[l,mid]对f[i]的贡献,
h
i
,
l
=
∑
j
=
0
l
−
1
f
j
∗
a
i
−
j
h_{i, l}=\sum_{j=0}^{l-1}f_j*a_{i-j}
hi,l=∑j=0l−1fj∗ai−j
由定义:
f
i
=
∑
j
=
0
i
−
1
f
j
∗
a
i
−
j
=
∑
j
=
0
l
−
1
f
j
∗
a
i
−
j
+
∑
j
=
m
i
d
+
1
i
−
1
f
j
∗
a
i
−
j
+
g
i
=
h
i
,
l
+
∑
j
=
m
i
d
+
1
i
−
1
f
j
∗
a
i
−
j
+
∑
j
=
l
m
i
d
f
j
∗
a
i
−
j
\begin{aligned} f_i&=\sum_{j=0}^{i-1}f_j*a_{i-j}\\ &=\sum_{j=0}^{l-1}f_j*a_{i-j}+\sum_{j=mid+1}^{i-1}f_j*a_{i-j}+g_i\\ &=h_{i, l}+\sum_{j=mid+1}^{i-1}f_j*a_{i-j}+\sum_{j=l}^{mid}f_j*a_{i-j}\\ \end{aligned}
fi=j=0∑i−1fj∗ai−j=j=0∑l−1fj∗ai−j+j=mid+1∑i−1fj∗ai−j+gi=hi,l+j=mid+1∑i−1fj∗ai−j+j=l∑midfj∗ai−j
由
c
d
q
(
l
,
r
)
cdq(l,r)
cdq(l,r)的过程可知,
h
i
,
l
h_{i,l}
hi,l已知,
∑
j
=
m
i
d
+
1
i
−
1
f
j
∗
a
i
−
j
\sum_{j=mid+1}^{i-1}f_j*a_{i-j}
∑j=mid+1i−1fj∗ai−j将会在
c
d
q
(
m
i
d
+
1
,
r
)
cdq(mid+1,r)
cdq(mid+1,r)中计算出,所以只需求
{
f
l
,
f
l
+
1
,
.
.
.
,
f
m
i
d
}
∗
{
a
1
,
a
2
,
.
.
.
,
a
r
−
l
+
1
}
\{f_l,f_{l+1},...,f_{mid}\}*\{a_1,a_2,...,a_{r-l+1}\}
{fl,fl+1,...,fmid}∗{a1,a2,...,ar−l+1}即可。
c
d
q
(
l
,
r
)
cdq(l,r)
cdq(l,r)的过程时间复杂度为
O
(
l
o
g
2
n
)
O(log_2n)
O(log2n),FFT的时间复杂度为
O
(
n
l
o
g
2
n
)
O(nlog_2n)
O(nlog2n),故总时间复杂度为
O
(
n
l
o
g
2
2
n
)
O(nlog_2^2n)
O(nlog22n)
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int mn = 100005, mod = 313;
const double pi = acos(-1.0);
struct com{
double r, i;
com(double a = 0, double b = 0) : r(a), i(b) {}
com operator +(const com& t) {return com(r + t.r, i + t.i);}
com operator -(const com& t) {return com(r - t.r, i - t.i);}
com operator *(const com& t) {return com(r * t.r - i * t.i, r * t.i + i * t.r);}
}x[mn << 1], y[mn << 1], w, wn;
int a[mn], f[mn];
void change(com *x, int n)
{
for(int i = 1, j = n >> 1; i < n - 1; i++)
{
if(i < j)
swap(x[i], x[j]);
int k = n >> 1;
while(j >= k)
j -= k, k >>= 1;
j += k;
}
}
void fft(com x[], int n, int flag)
{
change(x, n);
for(int i = 2; i <= n; i <<= 1)
{
wn = com(cos(flag * 2 * pi / i), sin(flag * 2 * pi / i));
for(int j = 0; j < n; j += i)
{
w = com(1, 0);
for(int k = j; k < j + i / 2; k++)
{
com u = w * x[k + i / 2], v = x[k];
x[k] = v + u, x[k + i / 2] = v - u, w = w * wn;
}
}
}
if(flag == -1)
for(int i = 0; i < n; i++)
x[i].r /= n;
}
void cdq(int l, int r)
{
if(l == r)
{
f[l] += a[l], f[l] %= mod;
return;
}
int mid = (l + r) >> 1, i;
cdq(l, mid);
int len = 1;
while(len <= r - l + 1)
len <<= 1;
for(i = 0; i < len; i++)
x[i] = y[i] = com(0, 0);
for(i = l; i <= mid; i++)
x[i - l] = com(f[i], 0);
for(i = 1; i <= r - l + 1; i++)
y[i - 1] = com(a[i], 0);
fft(x, len, 1), fft(y, len, 1);
for(i = 0; i < len; i++)
x[i] = x[i] * y[i];
fft(x, len, -1);
for(i = mid + 1; i <= r; i++)
f[i] += int(x[i - l - 1].r + 0.5), f[i] %= mod;
cdq(mid + 1, r);
}
int main()
{
int n, i;
while(~scanf("%d", &n) && n)
{
for(i = 1; i <= n; i++)
scanf("%d", &a[i]), a[i] %= mod, f[i] = 0;
cdq(0, n);
printf("%d\n", f[n]);
}
}