CSP2020贪吃蛇 贪心

周末偷偷看了看CSP T4,发现非常刺激,铁牌退役狗想了一中午(不过感觉比树上的树简单不少) 注意到本题中的蛇只需要最大化自己吃几个,不需要最大化排名。我们称拥有其它蛇梦寐以求的选择权利的蛇为后浪蛇(当前值最大),没有选择权利的蛇为打工蛇(当前值最小)。 考虑后浪蛇的策略:如果吃掉打工蛇,在游戏之后一定不会死,那就吃,否则就不吃。于是只需要求会不会死。 可以发现吃掉打工蛇之后,如果自己没有变成打工蛇,那么它的值一定大于之后产生的所有新蛇,于是不会死可以吃。如果吃掉后只剩下自己,也可以吃。 如果后浪蛇吃掉打工蛇后自己变成打工蛇,那么它很可能被下一个吃掉。于是只需判断下一个后浪蛇是否可以吃掉它。如果吃掉当前打工蛇,下一个后浪蛇可以吃掉自己,那么就必须停止游戏。否则就可以吃。于是可以假装吃掉了打工蛇,把问题丢给下一个后浪,递归求解。递归边界是某条后浪吃掉打工蛇后自己没有变成打工蛇。 当然其实不需要递推,开个set模拟就行。注意到模拟上述过程时间不太够,考虑用某年noip蚯蚓那个题的经典方法优化。具体来说,开两个deque,由上面的分析可知产生的新蛇一定更小,于是把初始蛇丢尽第一个队列,新产生的蛇丢进第二个队列,假装它是个set就行了。

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
#include <bits/stdc++.h>
using std::cerr;
using std::endl;

inline int rd() {
int b = 0; char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) b = b * 10 + c - '0', c = getchar();
return b;
}

const int N = 1e6 + 233;

struct Node {
int val, id;
bool operator<(Node n) const {
return val < n.val (val == n.val && id < n.id);
}
bool operator==(Node n) const {
return val == n.val && id == n.id;
}
};

int ans, n, A[N];

inline void solve(int kase) {
ans = rd();
if (kase == 1) {
n = ans;
for (int i = 1; i <= n; ++i)
A[i] = rd();
} else {
for (int i = 1; i <= ans; ++i) {
int x = rd(), y = rd();
A[x] = y;
}
}
ans = n;
std::deque<Node> que1, que2;
for (int i = 1; i <= ans; ++i)
que1.push_front({A[i], i});
while (true) {
if (que1.size() + que2.size() <= 2) {
if (que1.size() + que2.size() == 2)
--ans;
break;
}
Node min, max;
min = que1.back();
que1.pop_back();
if (que2.empty()) {
max = que1.front();
que1.pop_front();
} else {
if (que1.size() == 1 que1.front() < que2.front()) {
max = que2.front();
que2.pop_front();
} else {
max = que1.front();
que1.pop_front();
}
}
max.val -= min.val;
if (!que1.empty() && que1.back() < max) {
que2.push_back(max);
--ans;
continue;
}
int del = 0;
while (true) {
if (que1.size() + que2.size() == 1)
break;
min = max;
if (que2.empty()) {
max = que1.front();
que1.pop_front();
} else {
if (que1.size() == 1 que1.front() < que2.front()) {
max = que2.front();
que2.pop_front();
} else {
max = que1.front();
que1.pop_front();
}
}
max.val -= min.val;
if (!que1.empty() && que1.back() < max)
break;
if (!que2.empty() && que2.back() < max)
break;
del ^= 1;
}
ans -= del;
break;
}
printf("%d\n", ans);
}

int main() {
int T = rd();
for (int i = 1; i <= T; ++i)
solve(i);
return 0;
}