Codeforces Round #600 (Div. 2)
/ / 阅读耗时 18 分钟 Codeforces Round #600 (Div. 2)题解。论如何会做五道题,比赛时做出四道,还FST两道。
A. Single Push
对于这种问题,我们暴力就可以。具体来说找出不一样的地方,然后暴力地判它合不合法。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
using namespace std;
int a[100005], b[100005], n;
vector<int> vec;
inline bool isOk()
{
for (int i = 1; i < vec.size(); i++) {
if (vec[i] - vec[i - 1] != 1)
return false;
}
return true;
}
inline int isOk2()
{
if (vec.size() == 0)
return 1;
if (vec.size() == 1 && b[vec[0]] > a[vec[0]])
return 1;
int s = b[vec[0]] - a[vec[0]];
if (s < 0)
return 0;
for (int i = 1; i < vec.size(); i++) {
if (b[vec[i]] - a[vec[i]] != s)
return 0;
}
return 1;
}
int main()
{
int t;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", a + i);
for (int i = 1; i <= n; i++)
scanf("%d", b + i);
vec.clear();
for (int i = 1; i <= n; i++) {
if (a[i] != b[i])
vec.push_back(i);
}
if (isOk() && isOk2()) {
printf("YES\n");
}
else {
printf("NO\n");
}
}
// PAUSE;
return 0;
}
B. Silly Mistake
这个题,啊,FST了。
其实就是一个模拟,这里注意一下由于题目没有最小化天数的要求,当我们发现所有人都走了的时候,就开始下一天,这样能保证正确性。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
using namespace std;
int n, op[100005];
set<int> in, out;
vector<int> ans;
int main()
{
// DEBUG;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", op + i);
int num = 0;
for (int i = 1; i <= n; i++) {
if (in.empty()) {
if (num)
ans.push_back(num);
num = 0;
in.clear();
out.clear();
}
if (op[i] > 0) {
if (in.count(op[i]) || out.count(op[i])) {
printf("-1");
return 0;
}
in.insert(op[i]);
++num;
}
else {
if (in.count(-op[i]) == 0) {
printf("-1");
return 0;
}
in.erase(-op[i]), ++num, out.insert(-op[i]);
}
}
if (!in.empty()) {
printf("-1");
return 0;
}
if (num != 0)
ans.push_back(num);
printf("%d\n", ans.size());
for (int i = 0; i < ans.size(); i++) {
printf("%d ", ans[i]);
}
// PAUSE;
return 0;
}
C. Sweets Eating
这个题确实挺有意思的。
可以看得出来,应该排个序,然后贪心。让甜度较大的尽可能在前面,甜度较小的在后面。
不过题目要求整整$n$天的,这样做复杂度太高。
注意到从第$k$个答案更新第$k+1$个时,变化的除了新加入的那一个值之外,还有所有“块”的最后一个(所谓块就是每一天的那$m$个连续的糖果),可以发现这些发生变化的糖果的序号呈公差为$m$的等差数列,并且和是前缀和的形式,这样我们维护一个前缀和,在加入糖果时更新,就可以$O(n)$地做这题。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
using namespace std;
long long n, op[200005], m;
long long ans[200005];
vector<long long> vec[200005];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", op + i);
sort(op + 1, op + n + 1);
for (int i = 1; i <= m; i++) {
long long s = 0;
vec[i].push_back(0);
for (int j = i; j <= n; j += m) {
s += op[j];
vec[i].push_back(s);
}
}
int at, num;
ans[1] = op[1];
for (int i = 2; i <= m; i++) {
ans[i] = ans[i - 1] + op[i];
}
at = 1, num = 1;
for (int i = m + 1; i <= n; i++) {
ans[i] = ans[i - 1] + op[i] + vec[at][num];
if (at == m) {
at = 1, ++num;
}
else
++at;
}
for (int i = 1; i <= n; i++)
printf("%lld ", ans[i]);
// PAUSE;
return 0;
}
D. Harmonious Graph
首先来一遍$DFS$染色。
紧接着,从$n$开始倒着扫,找出它所在连通块的最小编号节点,假设为$s$,易知区间$[s,n]$中的所有点都应该与$n$连通,找出那些不连通的连通块数目,加到答案中,同时更新$s$,这样扫下去,直到遍历整个区间。
连通性可以用并查集维护,复杂度$O(n)$。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
using namespace std;
struct Edge
{
int next, to;
} edge[400005];
set<int> ssp;
int n, m, head[200005], cnt = 1, color[200005], ID, vis[200005];
int last[200005], fa[200005];
void DFS(int x)
{
for (int i = head[x]; i; i = edge[i].next) {
if (!vis[edge[i].to]) {
vis[edge[i].to] = 1;
color[edge[i].to] = color[x];
DFS(edge[i].to);
}
}
}
inline void add(int x, int y)
{
edge[cnt].next = head[x], edge[cnt].to = y, head[x] = cnt++;
}
int ff(int x)
{
return fa[x] == x ? x : fa[x] = ff(fa[x]);
}
int main()
{
// DEBUG;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
for (int i = n; i >= 1; i--) {
if (!vis[i])
vis[i] = 1, color[i] = ++ID, DFS(i);
}
for (int i = 1; i <= ID; i++)
fa[i] = i;
for (int i = 1; i <= n; i++) {
if (last[color[i]] == 0)
last[color[i]] = i;
}
int ans = 0;
for (int i = n, up = last[color[n]];;) {
int tmp = up;
for (int j = up; j <= i; j++) {
if (ff(color[j]) != ff(color[up]))
ssp.insert(color[j]);
}
ans += ssp.size();
i = up - 1;
for (int a : ssp) {
fa[ff(a)] = ff(color[tmp]);
up = min(last[a], up);
}
ssp.clear();
if (i < 1)
break;
if (up > i) {
up = last[color[i]];
}
}
cout << ans;
// PAUSE;
return 0;
}
E. Antenna Coverage
稍微有一点难的$DP$(其实并没有)。
首先明确这样一个观点:若一段区间是空白的(就是没有被覆盖),并且这一段空白的右侧有天线,我们应延长右侧的天线而非左侧。
而对于右侧没有天线的空白,只能延长左侧的天线了。
根据这个思想,我们可以列出这样一个状态:令$DP(x)$表示$[1,x-1]$都已经被覆盖,现在要覆盖$[x,m]$所需的最佳答案。
那么就有这样的一个状态转移方程:
当上式不存满足条件的$i$时,$DP(x)=m-x+1$。
复杂度$O(nm)$。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
using namespace std;
int n, m, s[100005], dp[100005];
struct P
{
int a, b;
} p[85];
int DP(int x)
{
if (x > m)
return 0;
if (~dp[x])
return dp[x];
if (s[x])
return dp[x] = DP(x + 1);
dp[x] = 0x3f3f3f3f;
bool lk = true;
for (int i = 1; i <= n; i++) {
if (p[i].a - p[i].b > x) {
lk = false;
dp[x] =
min(dp[x], p[i].a - x - p[i].b + DP(p[i].a + p[i].a - x + 1));
}
}
if (lk) {
dp[x] = m - x + 1;
}
return dp[x];
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &p[i].a, &p[i].b);
}
for (int i = 1; i <= n; i++) {
++s[max(p[i].a - p[i].b, 1)];
--s[min(m, p[i].a + p[i].b) + 1];
}
for (int i = 1; i <= m; i++)
s[i] += s[i - 1];
memset(dp, -1, sizeof(dp));
cout << DP(1);
// PAUSE;
return 0;
}
F. Cheap Robot
好题(我不会做的都是好题)。
从$1$到$k$节点跑一个多源$Dijkstra$求最短路,可以求出点$u$到充电站的最近距离,记为$d_u$。
注意到,若机器人在$u$处,其电量不会多于$c-d_u$。并且,当机器人电量少于$d_u$时,将无解。若机器人电量不少于$d_u$,它一定可以补充电量到$c-d_u$,因此我们可以认为机器人时时刻刻保持$c-d_u$的电量。
如果机器人可以从$u$走到$v$并且答案可行,那么必有:
等价于:
这就是一条边合法的充要条件。
于是我们重建图,将所有边的边权更新为$d_u+d_v+w$。问题转化为查询$a_i$到$b_i$的所有路径上最大值的最小值。
这是一个经典为题。方式是求最小生成树,然后在树上求两点之间路径上的最大值,可以用倍增法也可以用树链剖分,甚至LCT都可以。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
129
using namespace std;
struct Graph
{
struct Edge
{
int next, to;
long long v;
};
Edge e[500005 << 1];
int cnt = 1, head[100005];
inline void add(int x, int y, long long v)
{
e[cnt].next = head[x], e[cnt].v = v;
e[cnt].to = y, head[x] = cnt++;
}
} g;
struct E
{
int from, to;
long long dis;
bool operator<(E e)
{
return dis < e.dis;
}
};
struct Node
{
int at;
long long dis;
Node(int a, long long s) : at(a), dis(s) {}
bool operator<(const Node& s) const
{
return dis > s.dis;
}
};
priority_queue<Node> pq;
vector<E> vec;
int n, m, k, q, fa[100005], gr[20][100005], dep[100005];
long long dis[100005], maxn[20][100005];
int ff(int x)
{
return fa[x] == x ? x : fa[x] = ff(fa[x]);
}
void DFS(int x)
{
for (int i = g.head[x]; i; i = g.e[i].next) {
if (dep[g.e[i].to])
continue;
dep[g.e[i].to] = dep[x] + 1;
gr[0][g.e[i].to] = x;
maxn[0][g.e[i].to] = g.e[i].v;
for (int j = 1; j < 20; j++) {
gr[j][g.e[i].to] = gr[j - 1][gr[j - 1][g.e[i].to]];
maxn[j][g.e[i].to] =
max(maxn[j - 1][g.e[i].to], maxn[j - 1][gr[j - 1][g.e[i].to]]);
}
DFS(g.e[i].to);
}
}
int main()
{
scanf("%d%d%d%d", &n, &m, &k, &q);
for (int i = 1, x, y, z; i <= m; i++) {
scanf("%d%d%d", &x, &y, &z);
g.add(x, y, z);
g.add(y, x, z);
}
for (int i = 1; i <= k; i++)
g.add(n + 1, i, 0);
for (int i = 1; i <= n + 1; i++)
dis[i] = 1ll << 60;
dis[n + 1] = 0, pq.push(Node(n + 1, 0));
while (!pq.empty()) {
Node t = pq.top();
pq.pop();
if (t.dis != dis[t.at])
continue;
for (int i = g.head[t.at]; i; i = g.e[i].next) {
if (dis[g.e[i].to] > dis[t.at] + g.e[i].v) {
dis[g.e[i].to] = dis[t.at] + g.e[i].v;
pq.push(Node(g.e[i].to, dis[g.e[i].to]));
}
}
}
for (int i = 1; i <= n; i++) {
fa[i] = i;
for (int j = g.head[i]; j; j = g.e[j].next) {
E e;
e.from = i, e.to = g.e[j].to;
e.dis = dis[i] + dis[g.e[j].to] + g.e[j].v;
vec.push_back(e);
}
}
sort(vec.begin(), vec.end());
g.cnt = 1, memset(g.head, 0, sizeof(g.head));
for (auto s : vec) {
if (ff(s.from) != ff(s.to)) {
fa[ff(s.from)] = ff(s.to);
g.add(s.from, s.to, s.dis);
g.add(s.to, s.from, s.dis);
}
}
dep[1] = 1, DFS(1);
while (q--) {
int a, b;
scanf("%d%d", &a, &b);
if (dep[b] < dep[a])
swap(a, b);
long long ans = 0;
for (int i = 19; i >= 0; i--) {
if (dep[gr[i][b]] >= dep[a])
ans = max(ans, maxn[i][b]), b = gr[i][b];
}
if (a != b) {
for (int i = 19; i >= 0; i--) {
if (gr[i][a] != gr[i][b]) {
ans = max(ans, max(maxn[i][a], maxn[i][b]));
a = gr[i][a];
b = gr[i][b];
}
}
ans = max(ans, max(maxn[0][a], maxn[0][b]));
}
printf("%lld\n", ans);
}
// PAUSE;
return 0;
}