

「集训队作业」IOI 2020 集训队作业小结 4
source link: https://blunt-axe.github.io/2019/12/06/20191206-IOI2020-Homework-4/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文包括 IOI 2020 集训队作业 27 ~ 30 题的题解,下面是题单:
Lexicographic constraints
二分答案,暴力模拟,时间复杂度 O(nlog2n)O(nlog2n)。
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 2e5;
5
int n, a[maxn + 3];
6
7
bool check(int k) {
8
map<int, int> M;
9
for (int i = 1; i <= n; i++) {
if (a[i] <= a[i - 1]) {
if (k == 1) {
return false;
}
while (!M.empty() && M.rbegin() -> first > a[i]) {
map<int, int>::iterator it = M.end();
M.erase(--it);
}
for (int j = a[i]; j >= 0; j--) {
if (j == 0) {
20
return false;
21
}
22
if (++M[j] == k) {
23
M[j] = 0;
24
} else {
25
break;
26
}
27
}
28
}
29
}
30
return true;
31
}
32
33
int main() {
34
scanf("%d", &n);
35
for (int i = 1; i <= n; i++) {
36
scanf("%d", &a[i]);
37
}
38
int l = 1, r = n, mid;
39
while (l < r) {
40
mid = (l + r) >> 1;
41
if (check(mid)) {
42
r = mid;
43
} else {
44
l = mid + 1;
45
}
46
}
47
printf("%d\n", l);
48
return 0;
49
}
Wandering TKHS
考虑结点 uu 比父亲多走到了哪些点。设 pupu 表示 uu 的父亲,m(u)m(u) 表示路径 1−pu1−pu 中结点编号的最大值。令 Q(u,x)Q(u,x) 表示 uu 只经过 ≤x≤x 的点能走到多少个后代结点(uu 自己不算)。那么对于所有 u>1u>1,我们有:
原因是如果 u>m(pu)u>m(pu),那么以 pupu 为起点时就不会访问到 uu,我们就要新加入 uu 以及它子树中的信息。否则一定会访问到 uu,但是从 pupu 出发只会经过 ≤m(pu)≤m(pu) 的点,而从 uu 出发会经过 ≤m(u)≤m(u) 的点,所以要进行一定的修改。那么现在我们只要求出 Q(u,m(u)),Q(u,m(pu))Q(u,m(u)),Q(u,m(pu)) 即可。Q(u,x)Q(u,x) 有递归式:
发现直接递归 + 记忆化的复杂度是正确的。因为对于 Q(u,m(u))Q(u,m(u)),我们找到所有它后代中第一次 >m(u)>m(u) 的点并剪掉它这棵子树,之后 uu 的子树中点 vv 的询问就只剩下 Q(v,m(u))Q(v,m(u)) 了。也就是说每个点只会有 Q(u,m(u))Q(u,m(u)) 的询问。同理 Q(u,m(pu))Q(u,m(pu)) 递归到每个点也只有常数种询问,所以直接暴力即可,时间复杂度 O(nlogn)O(nlogn) 或 O(n)O(n)。
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 2e5;
5
int n, ans[maxn + 3], m[maxn + 3];
6
vector<int> G[maxn + 3];
7
map<pair<int, int>, int> M;
8
9
int Q(int u, int x, int p) {
if (M.count(make_pair(u, x))) {
return M[make_pair(u, x)];
}
int &ret = M[make_pair(u, x)] = 0;
for (int i = 0, v; i < G[u].size(); i++) {
v = G[u][i];
if (v == p || v > x) continue;
ret += Q(v, x, u) + 1;
}
return ret;
20
}
21
22
void dfs(int u, int p = 0) {
23
if (u != 1) {
24
ans[u] = ans[p];
25
if (u > m[p]) {
26
ans[u] += Q(u, m[u], p) + 1;
27
} else {
28
ans[u] += Q(u, m[u], p) - Q(u, m[p], p);
29
}
30
}
31
for (int i = 0, v; i < G[u].size(); i++) {
32
v = G[u][i];
33
if (v == p) continue;
34
m[v] = max(u, m[u]);
35
dfs(v, u);
36
}
37
}
38
39
int main() {
40
scanf("%d", &n);
41
for (int i = 1, u, v; i < n; i++) {
42
scanf("%d %d", &u, &v);
43
G[u].push_back(v), G[v].push_back(u);
44
}
45
dfs(1);
46
for (int i = 2; i <= n; i++) {
47
printf("%d%c", ans[i], " \n"[i == n]);
48
}
49
return 0;
50
}
Construction on a Tree
首先从每个集合中选一个数 aiai,使得选出的数构成 2,3,⋯,n2,3,⋯,n 的排列。然后对于每个集合的 x≠aix≠ai,连有向边 x→aix→ai,最后求出 11 号点为根的一棵生成树即可,如果不能选出排列或生成树就无解。考虑这样的正确性,首先不能选出排列显然是无解的,有生成树显然是有解的,如果没有生成树代表与某个大小为 xx 联通块和剩下 n−xn−x 个点没有任何关联,自然是无解的了。集合选数可以用网络流,所以复杂度 O(nn−−√)O(nn)。
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 1e6, inf = 1e9;
5
int n, tot, ter[maxn + 3], wei[maxn + 3], nxt[maxn + 3], lnk[maxn + 3];
6
int cur[maxn + 3], dep[maxn + 3], num[maxn + 3], ans[maxn + 3];
7
bool vis[maxn + 3];
8
vector<int> S[maxn + 3], V[maxn + 3], G[maxn + 3];
9
int adj(int x) {
return x & 1 ? x + 1 : x - 1;
}
int add(int u, int v, int w) {
ter[++tot] = v, wei[tot] = w;
nxt[tot] = lnk[u], lnk[u] = tot;
return tot;
}
20
int add_f(int u, int v) {
21
int t = add(u, v, 1);
22
return add(v, u, 0), t;
23
}
24
25
bool bfs() {
26
queue<int> Q;
27
Q.push(1);
28
memset(dep, -1, sizeof(dep));
29
dep[1] = 0;
30
while (!Q.empty()) {
31
int u = Q.front();
32
Q.pop();
33
for (int i = lnk[u], v, w; i; i = nxt[i]) {
34
v = ter[i], w = wei[i];
35
if ((~dep[v]) || !w) continue;
36
dep[v] = dep[u] + 1;
37
Q.push(v);
38
}
39
}
40
return ~dep[n * 2];
41
}
42
43
int dfs(int u, int t, int lft) {
44
if (u == t) {
45
return lft;
46
}
47
int ret = 0;
48
for (int &i = cur[u], v, w; i && ret < lft; i = nxt[i]) {
49
v = ter[i], w = wei[i];
50
if (w && dep[u] + 1 == dep[v]) {
51
int x = dfs(v, t, min(lft - ret, w));
52
wei[i] -= x, wei[adj(i)] += x, ret += x;
53
}
54
}
55
if (ret < lft) {
56
dep[u] = -1;
57
}
58
return ret;
59
}
60
61
int flow() {
62
int ret = 0;
63
while (bfs()) {
64
memcpy(cur, lnk, sizeof(cur));
65
ret += dfs(1, n * 2, inf);
66
}
67
return ret;
68
}
69
70
void dfs(int u) {
71
vis[u] = true;
72
for (int i = 0, v; i < G[u].size(); i++) {
73
v = G[u][i];
74
if (vis[v]) continue;
75
ans[v] = u;
76
dfs(v);
77
}
78
}
79
80
int main() {
81
scanf("%d", &n);
82
for (int i = 1, m; i <= n - 1; i++) {
83
scanf("%d", &m);
84
S[i].resize(m), V[i].resize(m);
85
for (int j = 0; j < m; j++) {
86
scanf("%d", &S[i][j]);
87
if (S[i][j] != 1) {
88
V[i][j] = add_f(S[i][j], i + n);
89
}
90
}
91
}
92
for (int i = 2; i <= n; i++) {
93
add_f(1, i);
94
}
95
for (int i = n + 1; i < n * 2; i++) {
96
add_f(i, n * 2);
97
}
98
if (flow() != n - 1) {
99
puts("-1");
return 0;
}
for (int i = 1; i <= n - 1; i++) {
int x = 0;
for (int j = 0; j < S[i].size(); j++) {
if (S[i][j] != 1 && !wei[V[i][j]]) {
x = S[i][j];
break;
}
}
num[i] = x;
for (int j = 0; j < S[i].size(); j++) {
if (S[i][j] != x) {
G[S[i][j]].push_back(x);
}
}
}
dfs(1);
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
puts("-1");
break;
}
}
for (int i = 1; i <= n - 1; i++) {
printf("%d %d\n", ans[num[i]], num[i]);
}
return 0;
}
Coloring Torus
对于 nn 为偶数,可以构造网格:
发现把一些 x+nx+n 换成 xx 也是对的,那么我们就可以取 n=2⌈K4⌉n=2⌈K4⌉,然后暴力搞掉几个 x+nx+n 就行了,注意特判 K=1K=1,时间复杂度 O(n2)O(n2)。
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 1000;
5
int n, k, c[maxn + 3], a[maxn + 3][maxn + 3];
6
7
int main() {
8
scanf("%d", &k);
9
if (k == 1) {
puts("1\n1");
return 0;
}
n = (((k - 1) >> 2) + 1) << 1;
for (int i = 0; i < n * 2; i++) {
c[i] = i;
}
for (int i = n * 2 - 1; i >= k; i--) {
c[i] -= n;
}
20
for (int i = 0; i < n; i++) {
21
for (int j = 0; j < n; j++) {
22
if (~i & 1) {
23
a[i][j] = c[(i + j) % n];
24
} else {
25
a[i][j] = c[(i + j) % n + n];
26
}
27
}
28
}
29
printf("%d\n", n);
30
for (int i = 0; i < n; i++) {
31
for (int j = 0; j < n; j++) {
32
printf("%d%c", a[i][j] + 1, " \n"[j == n - 1]);
33
}
34
}
35
return 0;
36
}
Recommend
-
36
● 集训队介绍与选拔 ● 集训讲师简介 ● 集训计划安排 集训队介绍 计蒜客集训队致力于公益性培养顶尖信息学选手,面向 N...
-
15
题目描述 我国的离婚率连续7年上升,今年的头两季,平均每天有近5000对夫妇离婚,大城市的离婚率上升最快,有研究婚姻问题的专家认为,是与简化离婚手续有关。 25岁的姗姗和男友谈恋爱半年就结婚,结婚不到两个月就离...
-
3
回归本源一一位运算及其应用 镇海中学 沈洋 2014年信息学奥林匹克中国国家队候选队员论文 2014国家集训队论文 完整版pdf请从本github仓库
-
7
IJUSTWANTAC's blog
-
6
ACM 国家集训队论文集(1999-2009) 腾讯微云下载(密码:m9xwtc) 陈宏:《数据结构的选择与算法效率——从IOI...
-
14
Unofficial list of IOI 2022 participants As in the previous years, I want to create a list of people participating in the next IOI. Please add participants from your country using this form:
-
3
本文包括 IOI 2020 集训队作业 31 ~ 35 题的题解,下面是题单: Inversion Sumf(i,j)f(i,j) 表示 ai>aja
-
12
Moldova IOI and EGOI teams Hello codeforces. As you could probably see in my previous posts, recently team selections ended in our country. Now the teams for IOI/BOI and EGOI are defined: IOI/BOI team:
-
11
IOI 2022 Day 2 Tasks → Pay attention
-
6
IOI 2022 Task Discussion (Editorial) → Pay attention
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK