M. Renaissance Past in Nancy
题目链接
题意:
T
≤
10
T \le 10
T≤10。有
n
≤
1
0
4
n \le 10^4
n≤104种物品,每种物品数量和体积分别为
1
≤
a
,
b
≤
1
0
3
1\le a,b\le10^3
1≤a,b≤103,
m
≤
1
0
4
m\le10^4
m≤104询问,每次询问区间
1
≤
l
,
r
≤
n
1\le l,r \le n
1≤l,r≤n的物品装体积为
c
≤
1
0
3
c\le 10^3
c≤103的方案数。
可逆背包:
对于背包问题,可以逆向转移而达到移除一个物品的贡献。
计数背包可逆,而求max背包不可逆。
背包与生成函数:
01背包:
F
(
x
)
=
1
+
x
b
F(x)=1+x^b
F(x)=1+xb
01退背包:
F
(
x
)
=
1
−
x
b
F(x)=1-x^b
F(x)=1−xb
完全背包:
F
(
x
)
=
1
1
−
x
b
F(x)=\frac{1}{1-x^b}
F(x)=1−xb1
多重背包:
F
(
x
)
=
1
−
x
(
a
+
1
)
b
1
−
x
b
F(x)=\frac{1-x^{(a+1)b}}{1-x^b}
F(x)=1−xb1−x(a+1)b
一堆物品生成函数:
G
(
x
)
=
∏
所
有
物
品
F
(
x
)
G(x)=\prod_{所有物品} F(x)
G(x)=∏所有物品F(x)
一堆物品,背包体积为
c
c
c的方案数
∑
i
=
0
v
g
i
=
(
G
(
x
)
1
−
x
)
[
c
]
\sum_{i=0}^vg_i=(\frac{G(x)}{1-x})[c]
∑i=0vgi=(1−xG(x))[c]
若
H
(
x
)
=
∑
i
=
0
h
i
x
i
H(x)=\sum_{i=0}h_ix^i
H(x)=∑i=0hixi,
H
(
x
)
1
−
x
=
∑
i
=
0
∑
j
=
0
i
h
j
x
i
\frac{H(x)}{1-x}=\sum_{i=0}\sum_{j=0}^ih_jx^i
1−xH(x)=∑i=0∑j=0ihjxi。
本题:
记第
i
i
i个物品的生成函数为
F
i
(
x
)
F_i(x)
Fi(x),
G
i
(
x
)
=
∏
j
=
1
i
F
j
(
x
)
G_i(x)=\prod_{j=1}^iF_j(x)
Gi(x)=∏j=1iFj(x)。
特别的
G
0
(
x
)
=
1
G_0(x)=1
G0(x)=1。
那么答案等于
(
1
1
−
x
G
r
(
x
)
∗
1
G
l
−
1
(
x
)
)
[
c
]
(\frac{1}{1-x}G_{r}(x)*\frac{1}{G_{l-1}(x)})[c]
(1−x1Gr(x)∗Gl−1(x)1)[c]。
G
i
(
x
)
G_i(x)
Gi(x)的求法:
从生成函数的角度,多重背包等价于体积为
b
b
b的完全背包和体积为
(
a
+
1
)
b
(a+1)b
(a+1)b的01退背包组成。
用背包DP的方法维护
G
i
(
x
)
G_i(x)
Gi(x),
O
(
c
)
O(c)
O(c)添加一个物品。
1
G
i
(
x
)
\frac{1}{G_i(x)}
Gi(x)1的求法:
类似的,多重背包的逆等价于体积为
(
a
+
1
)
b
(a+1)b
(a+1)b的完全背包和体积为
b
b
b的01退背包组成。
关于询问:
每次询问求一次卷积再求前缀和,复杂度太高。
预处理出
1
1
−
x
1
G
i
(
x
)
\frac{1}{1-x}\frac{1}{G_{i}(x)}
1−x1Gi(x)1或
1
1
−
x
G
i
(
x
)
\frac{1}{1-x}G_{i}(x)
1−x1Gi(x),(就是求前缀和啦),
O
(
n
c
)
O(nc)
O(nc);每次询问就只用求
(
1
1
−
x
G
r
(
x
)
∗
1
G
l
−
1
(
x
)
)
(\frac{1}{1-x}G_{r}(x)*\frac{1}{G_{l-1}(x)})
(1−x1Gr(x)∗Gl−1(x)1)的第
c
c
c项,
O
(
c
)
O(c)
O(c)解决。
最后,感谢这篇博客给的提示。
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
void insert(vector<int> &f, int a) {
for (int i = a; i <= f.size() - 1; i++) {
f[i] = (f[i] + f[i - a]) % mod;
}
}
void remove(vector<int> &f, int a) {
for (int i = f.size() - 1; i >= a; i--) {
f[i] = (f[i] - f[i - a] + mod) % mod;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int _;
cin >> _;
while (_--) {
int n, m;
cin >> n >> m;
const int V = 1000;
vector<vector<int>> f(n + 1, vector<int>(V + 1)), g(f);
f[0][0] = g[0][0] = 1;
for (int i = 1; i <= n; i++) {
int a, b;
cin >> b >> a;//b个a
f[i] = f[i - 1];
g[i] = g[i - 1];
insert(f[i], a);
remove(f[i], a * (b + 1));
remove(g[i], a);
insert(g[i], a * (b + 1));
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= V; j++)
f[i][j] = (f[i][j] + f[i][j - 1]) % mod;
}
int last = 0;
static int Case = 0;
cout << "Case #" << ++Case << ":\n";
while (m--) {
int l, r, c;
cin >> l >> r >> c;
int t1 = (l + last) % n + 1;
int t2 = (r + last) % n + 1;
l = min(t1, t2), r = max(t1, t2);
int res = 0;
for (int i = 0; i <= c; i++)
res = (res + (ll) g[l - 1][i] * f[r][c - i]) % mod;
last = res;
cout << res << "\n";
}
}
}