题解-LibreOJ-6515「雅礼集训 2018 Day10」贪玩蓝月

题目概括大力征集中~

提示:动态背包

一道神仙题。

Solution 1: 在线

类似动态 dp,本题要求对双端队列操作的同时进行若干次 01 背包处理。

考虑到双端队列的性质,我们可以维护两个栈,用来模拟双端队列。需要注意的是,如果在一个栈执行多次删除操作后该栈为空,则需将另一个栈中所有元素取出进行暴力重构。通过均摊复杂度分析可得复杂度仍为线性。

均摊复杂度证明?

记录动态规划数组 $f1[i][j]$ 和 $f2[i][j]$ ,分别表示取第 1/2 个栈中的前 $i$ 件物品得到的最有价值。该数组在栈中插入元素时需要暴力计算。

考虑如何统计答案。假设在左右栈中取出特征值总和分别为 $x,y$,则 $(x+y)\mod p\in [l,r]$。不难发现,在确定 $x,y$ 中的一个之后,另一个所在的范围即为一个区间。将对应的 $f$ 数组做一次单调队列统计答案即可。

注意:暴力重构栈时,若需要重构的栈长度为 1 ,则必须将该元素移动至另一个栈;否则会导致删除元素时出现问题

代码异常毒瘤,尤其是单调队列部分,强烈建议手推一遍。

代码如下:

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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 50005;
const ll inf = 1ll * 50005 * 1000000000;

int TestCase;
int M;
ll P;
pair<int, ll> s1[maxn], s2[maxn];
pair<int, ll> t[maxn];
ll f1[maxn][505], f2[maxn][505];
int len1, len2;
ll q[maxn], l, r;

inline void Add1(pair<int, ll> t) {
s1[++len1] = t;
int w = t.first; ll v = t.second;
for (int i = 0; i < P; ++i) {
f1[len1][i] = max(f1[len1 - 1][i], f1[len1 - 1][(i - w + P) % P] + v);
}
}

inline void Add2(pair<int, ll> t) {
s2[++len2] = t;
int w = t.first; ll v = t.second;
for (int i = 0; i < P; ++i) {
f2[len2][i] = max(f2[len2 - 1][i], f2[len2 - 1][(i - w + P) % P] + v);
}
}

inline void Rebuild1() {
int cnt = 0;
for (int i = 1; i <= len1; ++i)
t[++cnt] = s1[i];
int mid = cnt >> 1;
mid++;
for (int i = mid; i >= 1; --i)
Add2(t[i]);
len1 = 0;
for (int i = mid + 1; i <= cnt; ++i)
Add1(t[i]);
}

inline void Rebuild2() {
int cnt = 0;
for (int i = 1; i <= len2; ++i)
t[++cnt] = s2[i];
int mid = cnt >> 1;
mid++;
for (int i = mid; i >= 1; --i)
Add1(t[i]);
len2 = 0;
for (int i = mid + 1; i <= cnt; ++i)
Add2(t[i]);
}

inline void Solve(int ql, int qr) {
ll *x = f1[len1], *y = f2[len2];
ll ans = -1;
l = 0, r = 0, q[0] = 0;
for (int i = P - qr + ql; i < P; ++i) {
while (l < r && x[(q[r - 1] + P) % P] <= x[i]) r--;
q[r++] = i - P;
}
for (int i = 0; i < P; ++i) {
while (l < r && q[l] < i - qr + ql) l++;
while (l < r && x[(q[r - 1] + P) % P] <= x[i]) r--;
q[r++] = i;
ans = max(ans, x[(q[l] + P) % P] + y[(qr - i + P) % P]);
}
printf("%lld\n", ans);
}

int main() {
scanf("%d", &TestCase);
scanf("%d%lld", &M, &P);
for (int i = 1; i < P; ++i)
f1[0][i] = f2[0][i] = -inf;
while (M--) {
string op;
cin >> op;
if (op == "IF") {
int w; ll v;
scanf("%d%lld", &w, &v), w %= P;
Add1(make_pair(w, v));
} else if (op == "IG") {
int w; ll v;
scanf("%d%lld", &w, &v), w %= P;
Add2(make_pair(w, v));
} else if (op == "DF") {
if (len1 == 0) Rebuild2();
len1--;
} else if (op == "DG") {
if (len2 == 0) Rebuild1();
len2--;
} else if (op == "QU") {
int l, r;
scanf("%d%d", &l, &r);
Solve(l, r);
}
}
return 0;
}

Solution 2: 离线

离线解法

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×