只要你跑的够快,锅就追不上你

0%

「集训队作业」IOI 2020 集训队作业小结 5

简介

本文包括 IOI 2020 集训队作业 31 ~ 35 题的题解,下面是题单:

Inversion Sum

$f(i, j)$ 表示 $a_i > a_j$ 的概率,直接转移即可,时间复杂度 $O(n (n + q))$。

1
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 3000, mod = 1e9 + 7, inv2 = (mod + 1) >> 1;
5
int n, q, a[maxn + 3], f[maxn + 3][maxn + 3], g[maxn + 3][maxn + 3];
6
7
int main() {
8
	scanf("%d %d", &n, &q);
9
	for (int i = 1; i <= n; i++) {
10
		scanf("%d", &a[i]);
11
	}
12
	for (int i = 1; i < n; i++) {
13
		for (int j = i + 1; j <= n; j++) {
14
			f[i][j] = a[i] > a[j];
15
			f[j][i] = a[j] > a[i];
16
		}
17
	}
18
	int tot = 1;
19
	for (int x, y; q --> 0; ) {
20
		tot = 2ll * tot % mod;
21
		scanf("%d %d", &x, &y);
22
		for (int i = 1; i <= n; i++) {
23
			if (i != x) g[i][x] = f[i][x], g[x][i] = f[x][i];
24
			if (i != y) g[i][y] = f[i][y], g[y][i] = f[y][i];
25
		}
26
		f[x][y] = 1ll * (g[x][y] + g[y][x]) * inv2 % mod;
27
		f[y][x] = 1ll * (g[x][y] + g[y][x]) * inv2 % mod;
28
		for (int i = 1; i <= n; i++) if (i != x && i != y) {
29
			f[i][x] = 1ll * (g[i][x] + g[i][y]) * inv2 % mod;
30
			f[i][y] = 1ll * (g[i][x] + g[i][y]) * inv2 % mod;
31
			f[x][i] = 1ll * (g[x][i] + g[y][i]) * inv2 % mod;
32
			f[y][i] = 1ll * (g[x][i] + g[y][i]) * inv2 % mod;
33
		}
34
	}
35
	int sum = 0;
36
	for (int i = 1; i < n; i++) {
37
		for (int j = i + 1; j <= n; j++) {
38
			sum = (sum + f[i][j]) % mod;
39
		}
40
	}
41
	printf("%lld\n", 1ll * sum * tot % mod);
42
	return 0;
43
}

Less than 3

在每个相邻的 01 之间插红色隔板,10 之间插蓝色隔板,左右两边补上无限个隔板。这样给一个数取反相当于将一个隔板移动一格,那么我们枚举隔板的一个匹配,显然移动步数的下界是隔板距离之和。发现下界可以取到,所以直接计算取最小值即可,时间复杂度 $O(n^2)$。

1
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 1e4, inf = 1e9;
5
int n, k, l, a[maxn + 3], b[maxn + 3];
6
char s[maxn + 3], t[maxn + 3];
7
8
int _abs(int x) {
9
	return x < 0 ? -x : x;
10
}
11
12
int solve(int p, int q) {
13
	int ret = 0;
14
	for (int i = 0; i + p <= k || i + q <= l; i++) {
15
		int x = i + p <= k ? a[i + p] : n;
16
		int y = i + q <= l ? b[i + q] : n;
17
		ret += _abs(x - y);
18
	}
19
	for (int i = 1; p - i >= 1 || q - i >= 1; i++) {
20
		int x = p - i >= 1 ? a[p - i] : 0;
21
		int y = q - i >= 1 ? b[q - i] : 0;
22
		ret += _abs(x - y);
23
	}
24
	return ret;
25
}
26
27
int main() {
28
	scanf("%d %s %s", &n, s + 1, t + 1);
29
	for (int i = 1; i < n; i++) {
30
		if (s[i] != s[i + 1]) {
31
			a[++k] = i;
32
		}
33
	}
34
	for (int i = 0; i < 3; i++) {
35
		a[++k] = 0, a[++k] = n;
36
	}
37
	for (int i = 1; i < n; i++) {
38
		if (t[i] != t[i + 1]) {
39
			b[++l] = i;
40
		}
41
	}
42
	for (int i = 0; i < 3; i++) {
43
		b[++l] = 0, b[++l] = n;
44
	}
45
	if (t[1] != s[1]) {
46
		b[++l] = 0;
47
	}
48
	sort(a + 1, a + k + 1);
49
	sort(b + 1, b + l + 1);
50
	int ans = inf;
51
	for (int i = 1; i <= k; i += 2) {
52
		ans = min(ans, solve(i, 1));
53
	}
54
	for (int i = 1; i <= l; i += 2) {
55
		ans = min(ans, solve(1, i));
56
	}
57
	printf("%d\n", ans);
58
	return 0;
59
}

Permutation and Minimum

首先我们将每个不是 $-1$ 的 $p_i$ 变成 $2n - p_i + 1$ 方便处理。$(p_{2i - 1}, p_{2i})$ 可以分成三种情况:$(x, y), (x, -1), (-1, -1)$。$(x, y)$ 可以直接扔掉,我们先考虑不存在 $(x, -1)$ 的情况。可以看作是有 $2n$ 个点,将它们两两配对后右端点取值形成的集合的个数。考虑前 $i$ 个数,$i$ 可以匹配之前的某一个没选过的端点,或者自己暂时不选。发现方案数就是卡特兰数。当然对于一种匹配的方案,有 $n$ 对点,我们要把它分配到 $n$ 个位置,答案要额外地乘上 $n!$。

接下来考虑 $(x, -1)$,一个 $(x, -1)$ 就表示 $x$ 号点要匹配一个空的点,我们称 $x$ 为特殊点。记 $f(i, j, k)$ 表示考虑前 $i$ 个位置,有 $j$ 个未匹配的普通点,有 $k$ 个未匹配的特殊点。那么如果第 $i$ 个位置是特殊点,它要么暂时不选,要么匹配普通点。如果第 $i$ 个位置是普通点,它要么暂时不选,要么匹配普通点,要么匹配特殊点。注意匹配不同的特殊点时在原序列中的位置是不同的,所以方案数要乘上特殊点个数。那么最后的方案数就是 $f(n, 0, 0) \times c!$,$c$ 表示 $(-1, -1)$ 的对数。直接 dp,时间复杂度 $O(n^3)$。

1
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 300, maxm = maxn * 2, mod = 1e9 + 7;
5
int n, m, a[maxm + 3], cnt[maxm + 3], f[maxm + 3][maxn + 3][maxn + 3];
6
bool tag[maxm + 3];
7
8
void upd(int &x, int y) {
9
	x += y, x < mod ? 0 : x -= mod;
10
}
11
12
int main() {
13
	scanf("%d", &n);
14
	m = n * 2;
15
	for (int i = 1; i <= m; i++) {
16
		scanf("%d", &a[i]);
17
		if (a[i] != -1) {
18
			a[i] = m - a[i] + 1;
19
		}
20
		cnt[i] = 1;
21
	}
22
	for (int i = 1; i <= m; i += 2) {
23
		if (a[i] != -1 && a[i + 1] != -1) {
24
			cnt[a[i]] = 0, cnt[a[i + 1]] = 0;
25
		}
26
	}
27
	for (int i = 2; i <= m; i++) {
28
		cnt[i] += cnt[i - 1];
29
	}
30
	int num = 1, cur = 0;
31
	for (int i = 1; i <= m; i += 2) {
32
		if (a[i] == -1 && a[i + 1] == -1) {
33
			num = 1ll * num * ++cur % mod;
34
		} else if (a[i] == -1 || a[i + 1] == -1) {
35
			tag[cnt[a[i] + a[i + 1] + 1]] = true;
36
		}
37
	}
38
	m = cnt[m], n = m / 2;
39
	f[0][0][0] = 1;
40
	for (int i = 1; i <= m; i++) {
41
		for (int j = 0; j <= n; j++) {
42
			for (int k = 0; k <= n; k++) {
43
				if (tag[i]) {
44
					upd(f[i][j][k], f[i - 1][j + 1][k]);
45
					if (k > 0) {
46
						upd(f[i][j][k], f[i - 1][j][k - 1]);
47
					}
48
				} else {
49
					upd(f[i][j][k], f[i - 1][j + 1][k]);
50
					upd(f[i][j][k], 1ll * f[i - 1][j][k + 1] * (k + 1) % mod);
51
					if (j > 0) {
52
						upd(f[i][j][k], f[i - 1][j - 1][k]);
53
					}
54
				}
55
			}
56
		}
57
	}
58
	printf("%lld\n", 1ll * f[m][0][0] * num % mod);
59
	return 0;
60
}

A Sequence of Permutations

对于排列 $p, q$,定义 $pq$ 表示第 $i$ 位为 $p_{q_i}$ 的排列,$p^{-1}$ 表示第 $p_i$ 位为 $i$ 的排列,那么我们有 $(pq)^{-1} = q^{-1}p^{-1}$,$p^m p^n = p^{m + n}$,$(pq)r = p(qr)$,并且题目里的 $f(p, q) = qp^{-1}$。记 $g(p, q) = pqp^{-1}$,那么有:

也就是 $g(r,p)$ 和 $g(r,q)$ 在 $6$ 次之后会变成 $g(rqp^{-1}q^{-1}p,p)$ 和 $g(rqp^{-1}q^{-1}p,q)$,所以只要求出 $(qp^{-1}q^{-1}p)^{\lfloor \frac{k - 1}{6} \rfloor}$ 即可,时间复杂度 $O(n \log k)$ 或 $O(n)$。

1
#include <bits/stdc++.h>
2
using namespace std;
3
4
const int maxn = 1e5;
5
int n, k;
6
7
struct perm {
8
	int n, a[maxn + 3];
9
10
	perm(int m) {
11
		n = m;
12
		for (int i = 1; i <= n; i++) {
13
			a[i] = i;
14
		}
15
	}
16
17
	friend perm operator * (perm p, perm q) {
18
		perm r(p.n);
19
		for (int i = 1; i <= p.n; i++) {
20
			r.a[i] = p.a[q.a[i]];
21
		}
22
		return r;
23
	}
24
25
	perm inv() {
26
		perm r(n);
27
		for (int i = 1; i <= n; i++) {
28
			r.a[a[i]] = i;
29
		}
30
		return r;
31
	}
32
33
	void print() {
34
		for (int i = 1; i <= n; i++) {
35
			printf("%d%c", a[i], " \n"[i == n]);
36
		}
37
	}
38
};
39
40
perm g(perm p, perm q) {
41
	return p * q * p.inv();
42
}
43
44
perm qpow(perm p, int k) {
45
	perm q(n);
46
	for (; k; k >>= 1, p = p * p) {
47
		if (k & 1) q = p * q;
48
	}
49
	return q;
50
}
51
52
int main() {
53
	scanf("%d %d", &n, &k);
54
	perm p(n), q(n), r(n);
55
	for (int i = 1; i <= n; i++) {
56
		scanf("%d", &p.a[i]);
57
	}
58
	for (int i = 1; i <= n; i++) {
59
		scanf("%d", &q.a[i]);
60
	}
61
	int x = (k - 1) / 6;
62
	r = q * p.inv() * q.inv() * p;
63
	r = qpow(r, x);
64
	k -= x * 6;
65
	if (k == 1) {
66
		g(r, p).print();
67
	} else if (k == 2) {
68
		g(r, q).print();
69
	} else if (k == 3) {
70
		g(r, q * p.inv()).print();
71
	} else if (k == 4) {
72
		g(r, q * p.inv() * q.inv()).print();
73
	} else if (k == 5) {
74
		g(r, q * p.inv() * q.inv() * p * q.inv()).print();
75
	} else {
76
		g(r, q * p.inv() * q.inv() * p * p * q.inv()).print();
77
	}
78
	return 0;
79
}

Snuke the Phantom Thief

首先枚举选的总个数 $T$,对于一种选的方案记 x 坐标从小到大为 $x_1, x_2, \cdots, x_T$。那么 L $a_i, b_i$ 的限制相当于强制 $x_{b_i + 1} \ge a_i + 1$,R $a_i, b_i$ 的限制相当于强制 $x_{T - b_i} \le a_i - 1$。我们可以给每个 $x_i$ 加一个限制 $l_i \le x_i \le r_i$,并满足 $l_i \le l_{i + 1}, r_i \le r_{i + 1}$。D, U 的限制同理,我们搞出 $d_i \le y_i \le u_i$。然后我们建图:

  • 每个珠宝拆成 A 和 B,A 向 B 连边,容量为 $1$,费用为 $-v_i$。
  • 源点连向每个 x 限制,容量为 $1$,费用为 $0$。
  • 每个 x 限制向可行的珠宝 A 连边,容量为 $1$,费用为 $0$。
  • 每个珠宝 B 向可行的 y 限制连边,容量为 $1$,费用为 $0$。
  • 每个 y 限制连向汇点,容量为 $1$,费用为 $0$。

思考一下发现这样是对的,因为如果第 $i$ 个 x 限制选择的 x 坐标大于第 $i + 1$ 个 x 限制选择的 x 坐标的话一定是不优的。于是我们枚举 $T$,给每个费用预先加上 $10^{15}$,跑最小费用最大流,最后将答案减去 $10^{15}T$ 并取最小值即可。因为最大流的大小是 $O(n)$ 的,图的规模是 $O(n) - O(n^2)$ 的,我们还要跑 $n$ 遍这样的算法,所以时间复杂度为 $O(n^4)$。

1
#include <bits/stdc++.h>
2
using namespace std;
3
4
typedef long long ll;
5
const int maxn = 320, maxm = 1e5;
6
const ll lim = 1e15, inf = 1e18;
7
int n, x[maxn + 3], y[maxn + 3], m, a[maxn + 3], b[maxn + 3];
8
int l[maxn + 3], r[maxn + 3], u[maxn + 3], d[maxn + 3];
9
int tot, wei[maxm + 3], ter[maxm + 3], nxt[maxm + 3], lnk[maxn + 3];
10
int src, snk, pre[maxn + 3];
11
bool in[maxn + 3];
12
ll v[maxn + 3], len[maxm + 3], dist[maxm + 3];
13
char s[maxn + 3];
14
15
int func(int x) {
16
	return x & 1 ? x + 1 : x - 1;
17
}
18
19
void add(int u, int v, int w, ll l) {
20
	ter[++tot] = v, wei[tot] = w, len[tot] = l;
21
	nxt[tot] = lnk[u], lnk[u] = tot;
22
}
23
24
void add_f(int u, int v, int w, ll l) {
25
	add(u, v, w, l);
26
	add(v, u, 0, -l);
27
}
28
29
bool spfa() {
30
	queue<int> Q;
31
	Q.push(src), in[src] = true;
32
	fill(dist + 1, dist + snk + 1, inf);
33
	dist[src] = 0;
34
	while (!Q.empty()) {
35
		int u = Q.front();
36
		Q.pop();
37
		in[u] = false;
38
		ll l = 0;
39
		for (int i = lnk[u], v; i; i = nxt[i]) {
40
			v = ter[i], l = len[i];
41
			if (wei[i] && dist[u] + l < dist[v]) {
42
				dist[v] = dist[u] + l;
43
				pre[v] = i;
44
				if (!in[v]) {
45
					in[v] = true;
46
					Q.push(v);
47
				}
48
			}
49
		}
50
	}
51
	return dist[snk] != inf;
52
}
53
54
void flow(int &res, ll &ans) {
55
	while (spfa()) {
56
		int x = snk;
57
		while (x != src) {
58
			wei[pre[x]]--, wei[func(pre[x])]++;
59
			x = ter[func(pre[x])];
60
		}
61
		res++;
62
		ans += dist[snk];
63
	}
64
}
65
66
int main() {
67
	scanf("%d", &n);
68
	for (int i = 1; i <= n; i++) {
69
		scanf("%d %d %lld", &x[i], &y[i], &v[i]);
70
	}
71
	scanf("%d", &m);
72
	for (int i = 1; i <= m; i++) {
73
		char t[5];
74
		scanf("%s %d %d", t + 1, &a[i], &b[i]);
75
		s[i] = t[1];
76
		if (s[i] == 'U') {
77
			s[i] = 'D';
78
		} else if (s[i] == 'D') {
79
			s[i] = 'U';
80
		}
81
	}
82
	ll ans = 0;
83
	for (int T = 1; T <= n; T++) {
84
		fill(l + 1, l + T + 1, 1);
85
		fill(r + 1, r + T + 1, 100);
86
		fill(u + 1, u + T + 1, 1);
87
		fill(d + 1, d + T + 1, 100);
88
		for (int i = 1; i <= m; i++) {
89
			if (b[i] + 1 > T) {
90
				continue;
91
			}
92
			if (s[i] == 'L') {
93
				l[b[i] + 1] = max(l[b[i] + 1], a[i] + 1);
94
			} else if (s[i] == 'R') {
95
				r[T - b[i]] = min(r[T - b[i]], a[i] - 1);
96
			} else if (s[i] == 'U') {
97
				u[b[i] + 1] = max(u[b[i] + 1], a[i] + 1);
98
			} else if (s[i] == 'D') {
99
				d[T - b[i]] = min(d[T - b[i]], a[i] - 1);
100
			}
101
		}
102
		for (int i = 2; i <= T; i++) {
103
			l[i] = max(l[i], l[i - 1]);
104
			u[i] = max(u[i], u[i - 1]);
105
		}
106
		for (int i = T - 1; i; i--) {
107
			r[i] = min(r[i], r[i + 1]);
108
			d[i] = min(d[i], d[i + 1]);
109
		}
110
		src = n * 4 + 1, snk = src + 1;
111
		tot = 0;
112
		fill(lnk + 1, lnk + snk + 1, 0);
113
		for (int i = 1; i <= T; i++) {
114
			add_f(src, i, 1, 0);
115
			for (int j = 1; j <= n; j++) {
116
				if (x[j] >= l[i] && x[j] <= r[i]) {
117
					add_f(i, j + n, 1, 0);
118
				}
119
			}
120
		}
121
		for (int i = 1; i <= n; i++) {
122
			add_f(i + n, i + n * 2, 1, lim - v[i]);
123
		}
124
		for (int i = 1; i <= T; i++) {
125
			add_f(i + n * 3, snk, 1, 0);
126
			for (int j = 1; j <= n; j++) {
127
				if (y[j] >= u[i] && y[j] <= d[i]) {
128
					add_f(j + n * 2, i + n * 3, 1, 0);
129
				}
130
			}
131
		}
132
		int fl = 0;
133
		ll mx = 0;
134
		flow(fl, mx);
135
		if (fl == T && T * lim - mx > ans) {
136
			ans = T * lim - mx;
137
		}
138
	}
139
	printf("%lld\n", ans);
140
	return 0;
141
}