Educational Codeforces Round 112
(模拟赛)
题目 | A | B | C | D | E | F |
---|---|---|---|---|---|---|
通过情况 | AC | AC | AC | AC | O | O |
难度 | 900 | 1300 | 1300 | 1600 | 2100 | 2700 |
A. PizzaForces
题面
略
解法
注意特判$n<6$的情况
代码
1 | /* |
B. Two Tables
题面
略
解法
不难想到要得到最近的距离,只需平移第一个矩形,注意边界条件的判断
代码
1 | /* |
C. Coin Rows
题面
略
解法
利用前缀和逐点判断,确定下移的位置
代码
1 | /* |
D. Say No to Palindromes
题面
给定长度为$n$且仅由$a,b,c$组成的字符串,询问$m$次,每次询问$\left[ l,r \right] $的子串中使该子串的回文子串数量为$0$的最小修改次数(只能修改为$a,b,c$)
解法
发现要使回文子串数量为$0$,原串必须为$a,b,c$的某种排列循环连接而成,枚举这六种情况即可。
注意使用前缀和来加速询问($O\left( n \right)预处理 $$O\left( 1 \right) $询问)
代码
1 | /* |
E. Boring Segments
题面
略
解法
题目名字就非常耿直地告诉我们要用线段树。本题解法类似于扫描线,先将所有线段按数值排序,之后使用双指针逐一枚举情况。这样做的正确性在于任意移除两个指针之间的线段,如果不影响连通性,则不影响结果。这样就可以枚举出所有上下权值不同但连通的情况。
(具体实现见代码)
模拟比赛时由于线段树运用不熟练,没想明白单点和线段的区别(点有$m$个但线段只有$m-1$条),导致WA
代码
以线段建树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/*
* @题目: E. Boring Segments
* @算法: 线段树、双指针
* @Author: Blore
* @Language: c++11
* @Date: 2021-08-07 21:24:28
* @LastEditors: Blore
* @LastEditTime: 2021-08-07 21:35:39
*/
using namespace std;
int read()
{
int p = 0, f = 1;
char c = getchar();
while (c < 48 || c > 57)
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= 48 && c <= 57)
p = (p << 1) + (p << 3) + (c ^ 48), c = getchar();
return p * f;
}
int n, m;
struct LINE
{
int l, r, w;
} line[maxn];
int st[maxm << 2], tag[maxm << 2];
void pushdown(int rt)
{
if ((rt << 1 | 1) < (maxm << 2))
{
tag[rt << 1] += tag[rt];
tag[rt << 1 | 1] += tag[rt];
}
st[rt] += tag[rt];
tag[rt] = 0;
}
void pushup(int rt)
{
st[rt] = min(st[rt << 1], st[rt << 1 | 1]);
}
void upd(int rt, int l, int r, int ql, int qr, int c)
{
pushdown(rt);
if (ql > r || qr < l)
return;
if (ql <= l && r <= qr)
{
tag[rt] += c;
pushdown(rt);
return;
}
int mid = (l + r) >> 1;
upd(rt << 1, l, mid, ql, qr, c);
upd(rt << 1 | 1, mid + 1, r, ql, qr, c);
pushup(rt);
}
bool cmp(LINE x, LINE y)
{
return x.w < y.w;
}
int main()
{
n = read(), m = read() - 1;
rep(i, 1, n)
{
line[i].l = read(), line[i].r = read(), line[i].w = read();
}
sort(line + 1, line + n + 1, cmp);
int j = 1;
int ans = 0x7fffffff;
rep(i, 1, n)
{
while (j <= n && st[1] + tag[1] == 0)
{
upd(1, 1, m, line[j].l, line[j].r - 1, 1);
j++;
}
if (st[1] + tag[1] == 0)
break;
ans = min(ans, line[j - 1].w - line[i].w);
upd(1, 1, m, line[i].l, line[i].r - 1, -1);
}
printf("%d", ans);
return 0;
}
以单点建树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/*
* @题目: E. Boring Segments
* @算法: 线段树、双指针
* @Author: Blore
* @Language: c++11
* @Date: 2021-08-07 21:24:28
* @LastEditors: Blore
* @LastEditTime: 2021-08-07 21:31:03
*/
using namespace std;
int read()
{
int p = 0, f = 1;
char c = getchar();
while (c < 48 || c > 57)
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= 48 && c <= 57)
p = (p << 1) + (p << 3) + (c ^ 48), c = getchar();
return p * f;
}
int n, m;
struct LINE
{
int l, r, w;
} line[maxn];
int st[maxm << 2], tag[maxm << 2];
void pushdown(int rt)
{
if ((rt << 1 | 1) < (maxm << 2))
{
tag[rt << 1] += tag[rt];
tag[rt << 1 | 1] += tag[rt];
}
st[rt] += tag[rt];
tag[rt] = 0;
}
void pushup(int rt)
{
st[rt] = min(st[rt << 1], st[rt << 1 | 1]);
}
void upd(int rt, int l, int r, int ql, int qr, int c)
{
pushdown(rt);
if (ql >= qr)
return;
if (ql == l && r == qr)
{
tag[rt] += c;
pushdown(rt);
return;
}
int mid = (l + r) >> 1;
upd(rt << 1, l, mid, ql, min(qr, mid), c);
upd(rt << 1 | 1, mid, r, max(ql, mid), qr, c);
pushup(rt);
}
bool cmp(LINE x, LINE y)
{
return x.w < y.w;
}
int main()
{
n = read(), m = read();
rep(i, 1, n)
{
line[i].l = read(), line[i].r = read(), line[i].w = read();
}
sort(line + 1, line + n + 1, cmp);
int j = 1;
int ans = 0x7fffffff;
rep(i, 1, n)
{
while (j <= n && st[1] + tag[1] == 0)
{
upd(1, 1, m, line[j].l, line[j].r, 1);
j++;
}
if (st[1] + tag[1] == 0)
break;
ans = min(ans, line[j - 1].w - line[i].w);
upd(1, 1, m, line[i].l, line[i].r, -1);
}
printf("%d", ans);
return 0;
}
错误代码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/*
* @题目: E. Boring Segments
* @算法: 错误的线段树使用
* @Author: Blore
* @Language: c++11
* @Date: 2021-08-07 19:19:38
* @LastEditors: Blore
* @LastEditTime: 2021-08-07 21:19:29
*/
using namespace std;
int read()
{
int p = 0, f = 1;
char c = getchar();
while (c < 48 || c > 57)
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= 48 && c <= 57)
p = (p << 1) + (p << 3) + (c ^ 48), c = getchar();
return p * f;
}
int n, m;
struct LINE
{
int l, r, w;
} line[maxn];
int st[maxm << 2], tag[maxm << 2];
void pushdown(int rt)
{
if ((rt << 1 | 1) < (maxm << 2))
{
tag[rt << 1] += tag[rt];
tag[rt << 1 | 1] += tag[rt];
}
st[rt] += tag[rt];
tag[rt] = 0;
}
void pushup(int rt)
{
st[rt] = min(st[rt << 1], st[rt << 1 | 1]);
}
void upd(int rt, int l, int r, int ql, int qr, int c)
{
pushdown(rt);
if (ql > r || qr < l)
return;
if (ql <= l && r <= qr)
{
tag[rt] += c;
pushdown(rt);
return;
}
int mid = (l + r) >> 1;
upd(rt << 1, l, mid, ql, qr, c);
upd(rt << 1 | 1, mid + 1, r, ql, qr, c);
pushup(rt);
}
bool cmp(LINE x, LINE y)
{
return x.w < y.w;
}
int main()
{
n = read(), m = read();
rep(i, 1, n)
{
line[i].l = read(), line[i].r = read(), line[i].w = read();
}
sort(line + 1, line + n + 1, cmp);
int j = 1;
int ans = 0x7fffffff;
rep(i, 1, n)
{
while (j <= n && st[1] + tag[1] == 0)
{
upd(1, 1, m, line[j].l, line[j].r, 1);
j++;
}
if (st[1] + tag[1] == 0)
break;
ans = min(ans, line[j - 1].w - line[i].w);
upd(1, 1, m, line[i].l, line[i].r, -1);
}
printf("%d", ans);
return 0;
}
F. Good Graph
题面
向含有$n$个点的图中尝试加边权值为$0/1$的边,若图中所有的环边权值异或值为$1$,则加入图中,否则不加入
解法
首先要发现一个非常重要的性质:如果要满足条件,任意两个环不能有重合边。同时在接下来的操作中,将利用$k\oplus k=0$的异或性质。
将要加入的边分成两类:树边(加入后不产生新的环,即将不连通的块连在一起)和环边(加入后产生新的环)。显然在加入树边后对答案没有影响,所以先将所有树边加入(利用并查集判断)并预处理好$LCA$(倍增)以及从根节点到所有节点的边权异或结果(代码中的$xr$数组)
之后对环边的加入我们要处理两个问题:
①加入后的环边权异或值为1。求环的异或值我们利用xr数组就可以解决,即$xr\left[ u \right] \oplus xr\left[ v \right] \oplus w$
②加入后的环不与之前加入的环有重叠边。可以用树链剖分解决,不过这里标程很巧妙地用了$tin和tout$来计入dfs时每个点开始访问和结束访问的时间,一方面可以辅助$LCA$的求解,另一方面利用树状数组可以在$tin[lca]$到$tin[u/v]$的时间内是否有边加入(具体实现见代码)
代码
1 | /* |