← 全部文章

AtCoder Grand Contest 029 题解

本文目录(15 节)
  1. Irreversible operation
  2. Powers of two
  3. Lexicographic constraints
  4. 题意
  5. 题解
  6. 代码
  7. Grid game
  8. Wandering TKHS
  9. 题意
  10. 题解
  11. 代码
  12. Construction of a tree
  13. 题意
  14. 题解
  15. 代码

AtCoder Grand Contest 029

Irreversible operation

最后的字符串一定是所有 W 在 B 前面,于是考虑对应位置下标差之和即可。

const int N = 2e5 + 7;
int n, p[N], t;
char s[N];
ll ans;

int main() {
	rds(s, n);
	for (int i = 1; i <= n; i++)
		if (s[i] == 'B') p[++t] = i;
	for (int i = 1; i <= t; i++)
		ans += n - t + i - p[i];
	print(ans);
	return 0;
}

Powers of two

注意到 ≤x\le x 且能与 xx 匹配的数是唯一的,于是从大到小贪心即可。

const int N = 2e5 + 7, M = 31;
int n, a[N], b[N], ans;
map<int, int> c;

int main() {
	rd(n), rda(a, n);
	sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; i++) {
		++c[a[i]], b[i] = 1;
		while (b[i] <= a[i]) b[i] <<= 1;
		b[i] -= a[i];
	}
	for (int i = n; i; i--)
		if (c[a[i]]) {
			--c[a[i]];
			if (c[b[i]]) --c[b[i]], ++ans;
		}
	print(ans);
	return 0;
}

Lexicographic constraints

题意

  • 有 nn 个字符串 s1…ns_{1\dots n},对于 i∈[1,n−1]i \in [1,n-1],满足 si<si+1s_i < s_{i+1}。
  • 现在你不知道 s1…ns_{1\dots n},但你知道 sis_i 的长度为 aia_i。
  • 求最小的字符集大小,可以构造出合法的 s1…ns_{1\dots n}。
  • n≤2×105n \le 2 \times 10^5,ai≤109a_i \le 10^9。

题解

显然答案具有可二分性,先二分答案。

如果 ai>ai−1a_i > a_{i-1},则就是在 si−1s_{i-1} 的基础上后面加一堆 ai−ai−1a_i - a_{i-1} 个 11;否则,就是砍掉 si−1s_{i-1} 中 >ai> a_i 的部分然后 +1+1。

用栈维护 ss 中的每个相同字符连续段以及 +1+1 操作,根据势能分析复杂度为 O(n)\mathcal O(n)。

总时间复杂度 O(nlog⁡n)\mathcal O(n \log n)。

代码

const int N = 2e5 + 7, M = 31;
int n, a[N], p[N], c[N], s[N], t;

inline void add(int x, int k) {
	if (x == p[t]) c[t] += k, s[t] += k;
	else p[++t] = x, c[t] = k, s[t] = c[t] + s[t-1];
}

inline bool add1(int m) {
	if (t == 1 && p[t] == m) return 0;
	if (p[t] != m) {
		if (c[t] == 1) --t, add(p[t+1] + 1, 1);
		else --c[t], --s[t], add(p[t] + 1, 1);
	} else {
		int k = c[t--];
		if (c[t] == 1) --t, add(p[t+1] + 1, 1);
		else --c[t], --s[t], add(p[t] + 1, 1);
		add(1, k);
	}
	return 1;
}

inline bool pd(int m) {
	t = 0, add(1, a[1]);
	for (int i = 2; i <= n; i++)
		if (a[i] > a[i-1]) add(1, a[i] - a[i-1]);
		else {
			while (s[t] > a[i]) --t;
			if (s[t] < a[i]) add(p[t+1], a[i] - s[t]);
			if (!add1(m)) return 0;
		}
	return 1;
}

int main() {
	rd(n), rda(a, n);
	int l = 1, r = n;
	while (l < r) {
		int d = (l + r) >> 1;
		if (pd(d)) r = d;
		else l = d + 1;
	}
	print(l);
	return 0;
}

Grid game

显然先手一直会往下走,后手需要找到一个格子让先手无法走。

求出到每一行时后手可以让棋子在哪些列,如果这些列中存在某个位置有障碍,则让棋子走到这个障碍上方即可。

const int N = 2e5 + 7;
int n, m, k, p[N];

int main() {
	rd(n, m, k);
	for (int i = 1; i <= n; i++) p[i] = m + 1;
	for (int i = 1, x, y; i <= k; i++)
		rd(x, y), p[x] = min(p[x], y);
	for (int i = 2, r = 1; i <= n; i++)
		if (r >= p[i]) return print(i - 1), 0;
		else if (r + 1 < p[i]) ++r;
	print(n);
	return 0;
}

Wandering TKHS

题意

  • 给定一棵 nn 个点的树。
  • 你现在在某个点 rr,你要到 11 去。
  • 每次你会移动到未去过的,且与去过的点相邻的编号最小的点。
  • 你要求出对于 r∈[2,n]r \in [2,n],从 rr 到 11 的移动次数。
  • n≤2×105n \le 2 \times 10^5。

题解

把 11 当做根,设 fxf_x 表示 xx 的父亲,mxm_x 表示 1→fX1 \to f_X 的路径上编号最大的点,hx,yh_{x,y} 表示从 xx 开始不经过 fxf_x 和其它编号 >y>y 的点的情况下能到达多少个点,cxc_x 表示 xx 的答案。

考虑对于每个 xx,讨论一下可知 cx−cfxc_x - c_{f_x} 为 hx,mx−[x<mx][x<mfx]hx,mfxh_{x,m_x} - [x < m_x][x < m_{f_x}]h_{x,m_{f_x}}。

而对于 hh 有 hx,y=1+∑z∈son(x)[z≤y]hz,yh_{x,y} = 1 + \sum_{z \in \text{son}(x)} [z \le y]h_{z,y}。

注意到对于 hx,yh_{x,y},有用的 yy 值只有 mxm_x 和 mfXm_{f_X},因此总状态数为 O(n)\mathcal O(n),时间复杂度也为 O(n)\mathcal O(n)。

代码

const int N = 2e5 + 7;
int n, m[N], c[N];
vi e[N];
unordered_map<int, int> h[N];

void dfs1(int x, int f) {
	if (x == 1) m[x] = -1;
	else {
		m[x] = max(m[f], f), h[x][m[x]] = 0;
		if (x < m[x] && m[f] > x) h[x][m[f]] = 0;
	}
	for (pi o : h[x])
		for (int y : e[x])
			if (y < o.fi) h[y][o.fi] = 0;
	for (int y : e[x])
		if (y != f) dfs1(y, x);
	for (auto &o : h[x]) {
		++o.se;
		for (int y : e[x])
			if (y < o.fi) o.se += h[y][o.fi];
	}
	if (x == 1) return;
	if (x > m[x]) c[x] += h[x][m[x]];
	else {
		c[x] += h[x][m[x]];
		if (m[f] > x) c[x] -= h[x][m[f]];
	}
}

void dfs2(int x, int f) {
	c[x] += c[f];
	for (int y : e[x])
		if (y != f) dfs2(y, x);
}
 
int main() {
	rd(n);
	for (int i = 1, x, y; i < n; i++)
		rd(x, y), e[x].pb(y), e[y].pb(x);
	dfs1(1, 0), dfs2(1, 0);
	for (int i = 2; i <= n; i++)
		print(c[i], " \n"[i==n]);
	return 0;
}

Construction of a tree

题意

  • 给定 [1,n][1,n] 的 n−1n-1 个子集 E1…n−1E_{1\dots n-1}。
  • 问是否能从每个子集中选择两个元素,使得将这两个元素相连后形成一棵树,如果可以请构造一组方案。
  • n≤105n \le 10^5,∑i=1n−1∣Ei∣≤2×105\sum_{i=1}^{n-1} |E_i| \le 2 \times 10^5。

题解

考虑将 11 作为根,那么之后每条边都可以看成是一个点和其父亲的连边。

建立二分图,左边是点 2∼n2 \sim n,右边是子集 1∼n−11 \sim n - 1,若点 ii 在子集 jj 中则连边,那么如果有解就必须存在完备匹配。

任取一组完备匹配,从 11 开始构造树。一开始将 11 所在的所有点集的匹配点的父亲设为 11,然后从这些点递归下去。

如果递归到了所有点,则构造出了一组解,否则说明二分图不连通,从而一定无解。

Dinic 求二分图匹配,时间复杂度 O(mn)\mathcal O(m \sqrt n)。

代码

namespace Dinic {
	const int N = 2e5 + 7, M = 2e6 + 7;
	const ll inf = 1e18;
	int n, S, T, d[N];
	int h[N], hi[N], e[M], t[M], tot;
	ll f[M], mxf;

	inline void add(int x, int y, ll z, bool o = 1) {
		e[++tot] = y, f[tot] = z, t[tot] = h[x], h[x] = tot;
		if (o) add(y, x, 0, 0);
	}

	inline bool bfs() {
		for (int i = 1; i <= n; i++) d[i] = 0;
		queue<int> q;
		q.push(S), d[S] = 1;
		while (q.size()) {
			int x = q.front();
			q.pop();
			for (int i = h[x]; i; i = t[i]) {
				int y = e[i];
				ll z = f[i];
				if (d[y] || !z) continue;
				q.push(y), d[y] = d[x] + 1;
				if (y == T) return 1;
			}
		}
		return 0;
	}

	ll dfs(int x, ll now = inf) {
		if (x == T) return now;
		ll rst = now;
		for (int &i = hi[x]; i; i = t[i]) {
			int y = e[i];
			ll z = f[i];
			if (d[y] != d[x] + 1 || !z) continue;
			ll k = dfs(y, min(z, rst));
			if (!k) d[y] = 0;
			else f[i] -= k, f[i^1] += k, rst -= k;
			if (!rst) break;
		}
		return now - rst;
	}

	inline void main() {
		while (bfs()) {
			for (int i = 1; i <= n; i++) hi[i] = h[i];
			ll now;
			while ((now = dfs(S))) mxf += now;
		}
	}

	inline void init(int _n, int _S, int _T) {
		n = _n, S = _S, T = _T, tot = 1, mxf = 0;
		for (int i = 1; i <= n; i++) h[i] = 0; 
	}
}
using Dinic::add;

const int N = 1e5 + 7;
int n, f[N], fa[N], id[N];
set<int> e[N], g[N];
bool v[N];
pi ans[N];

int main() {
	rd(n), Dinic::init(2 * n, 1, 2 * n);
	for (int i = 1, c, x; i < n; i++) {
		add(1, i + 1, 1), add(i + n, 2 * n, 1);
		rd(c);
		while (c--) {
			rd(x), e[i].insert(x), g[x].insert(i);
			if (x == 1) continue;
			add(x, i + n, 1);
		}
	}
	Dinic::main();
	if (Dinic::mxf != n - 1) return print(-1), 0;
	for (int x = 1; x < n; x++)
		for (int i = Dinic::h[x+n]; i; i = Dinic::t[i])
			if (Dinic::e[i] <= n && Dinic::f[i]) f[x] = Dinic::e[i];
	queue<int> q;
	q.push(1);
	while (q.size()) {
		int x = q.front();
		q.pop();
		for (int y : g[x])
			if (!v[y]) v[y] = 1, fa[f[y]] = x, id[f[y]] = y, q.push(f[y]);
	}
	for (int i = 1; i < n; i++)
		if (!v[i]) return print(-1), 0;
	for (int i = 2; i <= n; i++) ans[id[i]] = mp(fa[i], i);
	for (int i = 1; i < n; i++) print(ans[i].fi, ans[i].se);
	return 0;
}

标签:DP、二分、二分图、博弈论、构造、树形 DP、网络流、贪心

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…