← 全部文章

AtCoder Grand Contest 034 题解

本文目录(15 节)
  1. Kenken Race
  2. ABC
  3. Tests
  4. Manhattan Max Matching
  5. 题意
  6. 题解
  7. 代码
  8. Complete Compress
  9. 题意
  10. 题解
  11. 代码
  12. RNG and XOR
  13. 题意
  14. 题解
  15. 代码

AtCoder Grand Contest 034

Kenken Race

分 c<dc < d 和 c>dc>d 讨论一下,后者合法多一个限制:存在 i∈[b,d]i \in [b,d] 满足 i−1,i,i+1i-1,i,i+1 都是空的。

const int N = 2e5 + 7;
int n, a, b, c, d;
char s[N];

inline bool pd(int i, int j) {
	while (i != j) {
		if (s[i+1] == '#' && s[i+2] == '#') return 0;
		if (s[i+1] == '.') i += 1;
		else i += 2;
	}
	return 1;
}

int main() {
	rd(n), rd(a, b), rd(c, d), rds(s, n);
	if (c < d) return prints(pd(a, c) && pd(b, d) ? "Yes" : "No"), 0;
	bool ok = 0;
	for (int i = b; i <= d; i++)
		if (s[i] == '.' && s[i-1] == '.' && s[i+1] == '.') ok = 1;
	prints(ok && pd(a, c) && pd(b, d) ? "Yes" : "No");
	return 0;
}

ABC

每次操作相当于将 BC 移到 A 后面,从前往后做就行了。

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

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

Tests

显然答案是若干个 xx,一个 [0,x][0,x],若干个 00。

二分答案,枚举 [0,x][0,x] 的那个找到最大值,时间复杂度 O(nlog⁡(nx))\mathcal O(n \log (nx))。

const int N = 1e5 + 7;
int n, x, p[N];
ll b[N], l[N], r[N], s[N], sum;

inline ll calc(int x, ll t) {
	return t <= b[x] ? t * l[x] : b[x] * l[x] + (t - b[x]) * r[x];
}

inline ll pd(ll t) {
	int w = t % x ? t % x : x, c = (t - w) / x;
	ll sum = 0, ret = 0;
	for (int i = 1; i <= c; i++) sum += s[p[i]];
	for (int i = c + 1; i <= n; i++)
		ret = max(ret, sum + calc(p[i], w));
	sum += s[p[c+1]];
	for (int i = 1; i <= c; i++)
		ret = max(ret, sum - s[p[i]] + calc(p[i], w));
	return ret;
}

int main() {
	rd(n, x);
	for (int i = 1; i <= n; i++)
		rd(b[i], l[i], r[i]), p[i] = i,
		s[i] = b[i] * l[i] + (x - b[i]) * r[i], sum += b[i] * l[i];
	sort(p + 1, p + n + 1, [&](int i, int j) { return s[i] > s[j]; });
	if (!sum) return print(0), 0;
	ll l = 1, r = 1ll * n * x;
	while (l < r) {
		ll mid = (l + r) >> 1;
		if (pd(mid) >= sum) r = mid;
		else l = mid + 1;
	}
	print(l);
	return 0;
}

Manhattan Max Matching

题意

  • 在平面上有 nn 个位置有红点,nn 个位置上有蓝点,其中 (rxi,ryi)(rx_i,ry_i) 上有 rcirc_i 个红点,(bxi,byi)(bx_i,by_i) 上有 bcibc_i 个蓝点,红蓝点总个数相同。
  • 现在要将红蓝点一一匹配,两点匹配的价值为其曼哈顿距离,要求最大价值和。
  • n≤103n \le 10^3,rci,bci≤10rc_i,bc_i \le 10。

题解

首先显然可以直接建图跑费用流,但边数为 O(n2)\mathcal O(n^2) 无法接受。

考虑曼哈顿距离有性质:

∣x1−x2∣+∣y1−y2∣=max⁡(x1−x2+y1−y2,x1−x2−y1+y2,−x1+x2+y1−y2,−x1+x2−y1+y2)=max⁡((x1+y1)+(−x2−y2),(x1−y1)+(−x2+y2),(−x1+y1)+(x2−y2),(−x1−y1)+(x2+y2)) \begin{aligned} |x_1-x_2|+|y_1-y_2| &=\max( x_1-x_2+y_1-y_2, x_1-x_2-y_1+y_2, -x_1+x_2+y_1-y_2, -x_1+x_2-y_1+y_2 )\\\\ &=\max( (x_1+y_1)+(-x_2-y_2), (x_1-y_1)+(-x_2+y_2), (-x_1+y_1)+(x_2-y_2), (-x_1-y_1)+(x_2+y_2) )\\\\ \end{aligned}

于是只需要建四个点对应每种情况,边数 O(n)\mathcal O(n),可以直接跑费用流。

代码

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

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

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

	ll dfs(int x, ll now = inf) {
		if (x == T) return now;
		v[x] = 1;
		ll rst = now;
		for (int &i = hi[x]; i; i = t[i]) {
			int y = e[i];
			ll z = f[i], w = c[i];
			if (v[y] || d[y] != d[x] + w || !z) continue;
			ll k = dfs(y, min(z, rst));
			if (!k) d[y] = inf;
			else ans += k * w, f[i] -= k, f[i^1] += k, rst -= k;
			if (!rst) break;
		}
		v[x] = 0;
		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, ans = 0;
		for (int i = 1; i <= n; i++) h[i] = 0; 
	}
}
using Dinic::add;
using Dinic::inf;

int main() {
	int n;
	rd(n);
	int p1 = n << 1 | 1, p2 = p1 + 1, p3 = p2 + 1, p4 = p3 + 1;
	int S = p4 + 1, T = S + 1;
	Dinic::init(T, S, T);
	for (int i = 1, x, y, c; i <= n; i++)
		rd(x, y, c),
		add(S, i, c, 0),
		add(i, p1, inf, x + y),
		add(i, p2, inf, x - y),
		add(i, p3, inf, -x + y),
		add(i, p4, inf, -x - y);
	for (int i = 1, x, y, c; i <= n; i++)
		rd(x, y, c),
		add(i + n, T, c, 0),
		add(p1, i + n, inf, -x - y),
		add(p2, i + n, inf, -x + y),
		add(p3, i + n, inf, x - y),
		add(p4, i + n, inf, x + y);
	Dinic::main();
	print(-Dinic::ans);
	return 0;
}

Complete Compress

题意

  • 给定一棵 nn 个点的树,树上的某些节点上有一个人。
  • 每次可以选择两个距离 ≥2\ge 2 的人,它们将向对方移动一个点。
  • 问能否使得所有人都在一个点上。
  • n≤2×103n \le 2 \times 10^3。

题解

枚举最终的点 rtrt,以 rtrt 为根建树。

则每一步必然是找到两个非祖先关系的人,向着它们的 lca\text{lca} 移动一格。

若将点 xx 上的人视为 dist(rt,x)\text{dist}(rt,x) 个,则问题等价于每次消掉两个非祖先关系的人,问是否能消完。

考虑 DP,设 fuf_u 表示 uu 子树的答案,设 dud_u 表示 uu 子树内的人数,则:

  • 若 uu 存在某个儿子 vv,满足 du<2dvd_u < 2d_v,则有转移 fu=du−dv+min⁡(fv,⌊2dv−du2⌋)f_u = d_u - d_v + \min(f_v, \lfloor\frac{2d_v - d_u}{2} \rfloor)。
  • 否则,fu=⌊du2⌋f_u = \lfloor\frac{d_u}{2} \rfloor。

最终要求 drtd_{rt} 为偶数,且 2frt≥drt2f_{rt} \ge d_{rt} 才能更新答案。

代码

const int N = 2e3 + 7, inf = 1e9;
int n, m, d[N], s[N], f[N], ans = inf;
char a[N];
vi e[N];

void dfs(int x, int fa = 0) {
	s[x] = a[x] == '1', d[x] = 0;
	int o = 0;
	for (int y : e[x])
		if (y != fa) {
			dfs(y, x), s[x] += s[y], d[x] += d[y] += s[y];
			if (d[y] > d[o]) o = y;
		}
	if (d[x] >= 2 * d[o]) f[x] = d[x] >> 1;
	else f[x] = d[x] - d[o] + min(f[o], (d[o] * 2 - d[x]) >> 1);
}

int main() {
	rd(n), rds(a, n);
	for (int i = 1; i <= n; i++) m += a[i] == '1';
	for (int i = 1, x, y; i < n; i++)
		rd(x, y), e[x].pb(y), e[y].pb(x);
	for (int x = 1; x <= n; x++) {
		dfs(x);
		if (d[x] & 1) continue;
		if (f[x] * 2 >= d[x]) ans = min(ans, d[x]);
	}
	print(ans == inf ? -1 : ans >> 1);
	return 0;
}

RNG and XOR

题意

  • 给定 nn 和 a0…2n−1a_{0\dots 2^n-1}。
  • 有一个初始为 00 的变量 xx。
  • 每次操作会将 xx 以 ai∑j=02n−1aj\frac {a_i}{\sum_{j=0}^{2^n-1}a_j} 的概率异或上 ii。
  • 求对于 i∈[0,2n−1]i \in [0,2^n-1],xx 第一次变成 ii 的期望操作次数。
  • n≤18n \le 18,ai≤103a_i \le 10^3,答案对 998244353998244353 取模。

题解

为了方便,将 aia_i 视为 ai∑j=02n−1aj\frac {a_i}{\sum_{j=0}^{2^n-1}a_j}。

设 xix_i 为 ii 第一次变成 00 的期望操作次数,显然 xix_i 也是 00 第一次变成 ii 的期望操作次数。

有转移: xi={0i=0∑j=02n−1ajxixor⁡j+1i≠0 x_i = \begin{cases} 0 & i = 0 \\\\ \sum_{j=0}^{2^n-1} a_jx_{i\operatorname{xor}j} + 1 & i \ne 0 \end{cases} 即异或卷积: (x0,x1,⋯ ,x2n−1)xor⁡(p0,p1,⋯ ,p2n−1)=(c,x1−1,x2−1,⋯ ,x2n−1−1) (x_0,x_1,\cdots,x_{2^n-1})\operatorname{xor}(p_0,p_1,\cdots,p_{2^n-1}) = (c,x_1-1,x_2-1,\cdots,x_{2^n-1}-1) 其中 cc 为一个常数。

注意到显然异或卷积 (p0,p1,⋯ ,p2n−1)(p_0,p_1,\cdots,p_{2^n-1}) 后总和不变,因此 c=x0+2n−1c = x_0+2^n-1,即: (x0,x1,⋯ ,x2n−1)xor⁡(p0,p1,⋯ ,p2n−1)=(x0+2n−1,x1−1,x2−1,⋯ ,x2n−1−1) (x_0,x_1,\cdots,x_{2^n-1})\operatorname{xor}(p_0,p_1,\cdots,p_{2^n-1}) = (x_0+2^n-1,x_1-1,x_2-1,\cdots,x_{2^n-1}-1) 注意到如果将 p0p_0 减 11,那么右边第 ii 项会减去 xix_i,正好把右边的未知项删掉了,即: (x0,x1,⋯ ,x2n−1)xor⁡(p0−1,p1,⋯ ,p2n−1)=(2n−1,−1,−1,⋯ ,−1) (x_0,x_1,\cdots,x_{2^n-1})\operatorname{xor}(p_0-1,p_1,\cdots,p_{2^n-1}) = (2^n-1,-1,-1,\cdots,-1) 这时异或卷积中的三项有两项都已知了,那么剩下的一项可以直接 FWT 求出。

然而会发现求出的答案不对,问题出在这样的异或卷积是有不只一组解的,对每个 xix_i 都加上 kk 之后式子依然成立。

又注意到 x0=0x_0 = 0,因此我们把求出来的每一项都减去 x0x_0 就是我们要的答案。

时间复杂度 O(n2n)\mathcal O(n2^n)。

代码

const int N = 1 << 18 | 1;
int n;
modint a[N], b[N], s;

inline void XOR(modint *f, modint x) {
	for (int o = 2, k = 1; o <= n; o <<= 1, k <<= 1)
		for (int i = 0; i < n; i += o)
			for (int j = 0; j < k; j++) {
				modint p = f[i+j] * x, q = f[i+j+k] * x;
				f[i+j] = p + q, f[i+j+k] = p - q;
			}
}

int main() {
	rd(n), n = 1 << n;
	for (int i = 0; i < n; i++) rd(a[i]), b[i] = P - 1, s += a[i];
	modint vs = 1 / s;
	for (int i = 0; i < n; i++) a[i] *= vs;
	a[0] -= 1, b[0] = n - 1;
	XOR(a, 1), XOR(b, 1);
	for (int i = 0; i < n; i++) b[i] /= a[i];
	XOR(b, (modint)1 / 2);
	for (int i = 0; i < n; i++) print(b[i] - b[0]);
	return 0;
}

标签:DP、FWT、二分、曼哈顿距离、树形 DP、概率期望、组合计数、网络流、贪心、费用流

评论

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

    正在连接评论服务…