← 全部文章

AtCoder Grand Contest 025 题解

本文目录(15 节)
  1. Digits Sum
  2. RGB Coloring
  3. Interval Game
  4. Choosing Points
  5. 题意
  6. 题解
  7. 代码
  8. Walking on a Tree
  9. 题意
  10. 题解
  11. 代码
  12. Addition and Andition
  13. 题意
  14. 题解
  15. 代码

AtCoder Grand Contest 025

Digits Sum

枚举。

inline int calc(int x) {
	int s = 0;
	while (x) s += x % 10, x /= 10;
	return s;
}

int main() {
	int n, ans = 1e9;
	rd(n);
	for (int i = 1; i < n; i++)
		ans = min(ans, calc(i) + calc(n - i));
	print(ans);
	return 0;
}

RGB Coloring

有 faa+fbb=kf_aa + f_bb = k,枚举 faf_a,如果存在 fbf_b,有贡献 (nfa)(bfb)\binom n{f_a}\binom b{f_b}。

int main() {
	int n;
	ll a, b, k;
	rd(n), rd(a, b, k), init(n);
	modint ans;
	for (int i = 0; i <= n && i * a <= k; i++)
		if (!((k - i * a) % b) && (k - i * a) / b <= n) {
			int j = (k - i * a) / b;
			ans += binom(n, i) * binom(n, j);
		}
	print(ans);
	return 0;
}

Interval Game

每次贪心地走,那么一定是左右左右地走,枚举第一步向左还是向右取 max⁡\max 即可。

const int N = 1e5 + 7;
int n, l[N], r[N];
bool v[N];

inline void work(int l, int r, int &t, ll &now) {
	if (l <= t && r >= t) return;
	if (t < l) now += l - t, t = l;
	else now += t - r, t = r;
}

inline ll solve(int o) {
	pq<pi> p, q;
	for (int i = 1; i <= n; i++)
		v[i] = 0, p.push(mp(l[i], i)), q.push(mp(-r[i], i));
	ll now = 0;
	int t = 0;
	for (int i = 1; i <= n; i++)
		if ((i & 1) == o) {
			while (v[p.top().se]) p.pop();
			int x = p.top().se;
			p.pop(), v[x] = 1;
			work(l[x], r[x], t, now);
		} else {
			while (v[q.top().se]) q.pop();
			int x = q.top().se;
			q.pop(), v[x] = 1;
			work(l[x], r[x], t, now);
		}
	work(0, 0, t, now);
	return now;
}

int main() {
	rd(n);
	for (int i = 1; i <= n; i++) rd(l[i], r[i]);
	print(max(solve(0), solve(1)));
	return 0;
}

Choosing Points

题意

  • 构造 n2n^2 个点,满足每个点的坐标都是 [0,2n)[0,2n) 中的整数,且任一对点的距离都既不是 d1\sqrt {d_1} 也不是 d2\sqrt {d_2}。
  • n≤300n \le 300,d1,d2≤2×105d_1,d_2 \le 2 \times 10^5。

题解

考虑将距离为 d\sqrt d 的点连边,可以证明得到的是一个二分图。

设 d=4kpd = 4^kp,其中 p mod 4≠0p \bmod 4 \ne 0。

若存在 x2+y2=dx^2+y^2 = d,则 (x,y)(x,y) 一定可以写成 (2ku,2kv)(2^ku, 2^kv) 的形式,其中 u2+v2=pu^2+v^2=p,反证很容易证明。

只考虑  mod 2k\bmod 2^k 的等价类。若 p mod 4=1p \bmod 4 = 1,则 u,vu,v 奇偶性不同,按 u+vu+v 的奇偶性染色即可;若 p mod 4=2p \bmod 4 = 2,则 u,vu,v 均为奇数,按 uu 的奇偶性染色即可;若 p mod 4=3p \bmod 4 = 3,则不存在合法点。

于是问题变成我们得到了两张二分图,要求取出一个包含 14\frac 14 的点集,考虑被两张二分图分成的四类中包含点数最多的一类即可。

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

代码

const int N = 607;
int n;
struct G {
	int d, c[N][N];
	vector<pi> p;
	inline bool pd(int x, int y) {
		return x >= 0 && x < n * 2 && y >= 0 && y < n * 2 && !~c[x][y];
	}
	void dfs(int x, int y, int z) {
		c[x][y] = z;
		for (pi o : p) {
			int X = x + o.fi, Y = y + o.se;
			if (pd(X, Y)) dfs(X, Y, z ^ 1);
		}
	}
	inline void main() {
		rd(d);
		for (int i = 0; i < n * 2; i++)
			for (int j = 0; j < n * 2; j++)
				c[i][j] = -1;
		for (int i = 1 - n * 2; i < n * 2; i++)
			for (int j = 1 - n * 2; j < n * 2; j++)
				if (i * i + j * j == d) p.pb(mp(i, j));
		for (int i = 0; i < n * 2; i++)
			for (int j = 0; j < n * 2; j++)
				if (pd(i, j)) dfs(i, j, 0);
	}
} g[2];
vector<pi> p[4];

int main() {
	rd(n), g[0].main(), g[1].main();
	for (int i = 0; i < n * 2; i++)
		for (int j = 0; j < n * 2; j++)
			p[g[1].c[i][j]*2+g[0].c[i][j]].pb(mp(i, j));
	int o = 0;
	for (int i = 1; i < 4; i++)
		if (p[i].size() > p[o].size()) o = i;
	while ((int)p[o].size() > n * n) p[o].pop_back();
	for (pi x : p[o]) print(x.fi, x.se);
	return 0;
}

Walking on a Tree

题意

  • 给定一棵 nn 个点的树和 mm 条未定向的路径。
  • 请你给这 mm 条路径定向,使得最大化有向覆盖的边数。
    • 有向覆盖,即一条边从不同方向走算作两条有向边被覆盖。
  • n,m≤2×103n,m \le 2 \times 10^3。

题解

猜测答案是上界 ∑i=1n−1min⁡(2,ci)\sum_{i=1}^{n-1} \min(2, c_i),考虑如何构造。

考虑每次缩掉一个叶子 uu,设其父亲为 fuf_u,与其相连的边为 ii。

若 ci=0c_i = 0 则可以直接缩。

若 ci=1c_i = 1 则怎么定向没有区别,把所有端点为 uu 的路径改成端点为 fuf_u,然后也可以直接缩。

若 ci≥2c_i \ge 2,则取出两条端点为 uu 的路径,剩下的路径按 ci=1c_i = 1 来。对于取出的两条路径,设为 (u,x),(u,y)(u,x),(u,y),则我们可以将它替换为 (x,y)(x,y)。若 (x,y)(x,y) 为 x→yx \to y,则还原为 x→ux \to u 和 u→yu \to y,反之亦然。

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

代码

const int N = 4e3 + 5;
int n, m, de[N], fa[N], ans;
vi e[N];
int u[N], v[N], fu[N], fv[N], f[N], g[N], tot;
bool w[N], o[N], r[N];

void dfs(int x) {
	for (int y : e[x])
		if (y != fa[x]) fa[y] = x, de[y] = de[x] + 1, dfs(y);
}

void mark(int a, int b, int c, int d) {
	static int t[N];
	memset(t, 0, sizeof(t));
	while (a != b) {
		if (de[a] < de[b]) swap(a, b);
		++t[a], a = fa[a];
	}
	while (c != d) {
		if (de[c] < de[d]) swap(c, d);
		++t[c], c = fa[c];
	}
	for (int i = 1; i <= n; i++) o[i] |= t[i] >= 2;
}

void work(int x) {
	for (int y : e[x])
		if (y != fa[x]) work(y);
	if (x == 1) return;
	int t = 0;
	for (int i = 1; i <= tot; i++)
		if (!w[i]) t += (fu[i] == x) ^ (fv[i] == x);
	ans += min(max(t, o[x] * 2), 2);
	if (t <= 1 || o[x]) {
		for (int i = 1; i <= tot; i++)
			if (!w[i]) {
				if (fu[i] == x) fu[i] = fa[x];
				if (fv[i] == x) fv[i] = fa[x];
			}
		return;
	}
	vi p;
	for (int i = 1; i <= tot; i++)
		if (!w[i] && ((fu[i] == x) ^ (fv[i] == x))) p.pb(i);
	int a = p[0], b = p[1];
	mark(fu[a], fv[a], fu[b], fv[b]);
	w[a] = w[b] = 1, f[++tot] = a, g[tot] = b;
	if (fu[a] == x) u[tot] = v[a], fu[tot] = fv[a];
	else u[tot] = u[a], fu[tot] = fu[a];
	if (fu[b] == x) v[tot] = v[b], fv[tot] = fv[b];
	else v[tot] = u[b], fv[tot] = fu[b];
	for (int i = 1; i <= tot; i++)
		if (!w[i]) {
			if (fu[i] == x) fu[i] = fa[x];
			if (fv[i] == x) fv[i] = fa[x];
		}
}

int main() {
	rd(n, m), tot = m;
	for (int i = 1, x, y; i < n; i++)
		rd(x, y), e[x].pb(y), e[y].pb(x);
	de[1] = 1, dfs(1);
	for (int i = 1; i <= m; i++)
		rd(u[i], v[i]), fu[i] = u[i], fv[i] = v[i];
	work(1), print(ans);
	for (int i = tot; i; i--) {
		if (!w[i]) r[i] = 1;
		if (i > m) {
			int a = f[i], b = g[i];
			r[a] = r[i] ^ (u[i] != u[a]);
			r[b] = r[i] ^ (v[i] != v[b]);
		}
	}
	for (int i = 1; i <= m; i++)
		if (r[i]) print(u[i], v[i]);
		else print(v[i], u[i]);
	return 0;
}

Addition and Andition

题意

  • 给定两个二进制数,分别为长度为 nn 的 xx 和长度为 mm 的 yy。
  • 你要进行下列操作 kk 次:
    • 设 z=xand⁡yz = x \operatorname{and} y,将 x,yx,y 都加上 zz。
  • 求最终 x,yx,y 的值。
  • n,m,k≤106n,m,k \le 10^6。

题解

令 si,tis_i,t_i 表示两个数的第 ii 位。

定义对 ii 的进位操作为:

  • 条件为 si=ti=1s_i = t_i = 1。
  • 将 si,tis_i,t_i 设为 22。
  • 重复下列操作:
    • 若 si=2s_i = 2 或 ti=2t_i = 2,将其设为 00 并对下一位 +1+1,将 ii 也 +1+1。
    • 否则结束进位操作。

定义一次加法操作为:

  • 从大到小对于每个 si=ti=1s_i = t_i = 1 的 ii 进行进位操作。

题目要求我们进行 kk 次加法操作,我们可以将其等价的转化为:

  • 从大到小对于每个 si=ti=1s_i = t_i = 1 的 ii 进行 kk 次下列操作:
    • 找到最小的满足 sj=tj=1s_j = t_j = 1 的 jj,对 jj 进行进位操作。
    • 若不存在这样的 jj,则结束操作。

可以这样转化是因为,在一次加法操作中,先进行的进位操作造成的增量将会大于之后进行的所有进位操作造成的增量之和,并且对 ii 进行进位操作后有 si=ti=0s_i = t_i = 0。

考虑转化后对于每个 si=ti=1s_i = t_i = 1 的 ii 我们具体要做什么。

设 x=00,01,10,11x = 00,01,10,11 为当前的进位情况,cc 表示剩余操作次数。

初始时让 si=ti=0s_i = t_i = 0,x=11x = 11,c=kc = k,然后对于 j=i,i+1,⋯j = i,i+1,\cdots 进行如下操作:

  • 若 (sj,tj)=(0,0)(s_j,t_j) = (0,0):
    • 若 x=10x = 10,让 (sj,tj)=(1,0)(s_j,t_j) = (1,0),结束操作。
    • 若 x=01x = 01,让 (sj,tj)=(0,1)(s_j,t_j) = (0,1),结束操作。
    • 若 x=11x = 11,让 (sj,tj)=(1,1)(s_j,t_j) = (1,1),xx 不变,cc 减去 11,若 c<0c < 0 结束操作。
  • 若 (sj,tj)=(1,0)(s_j,t_j) = (1,0):
    • 若 x=10x = 10,让 (sj,tj)=(0,0)(s_j,t_j) = (0,0),xx 不变。
    • 若 x=01x = 01,让 (sj,tj)=(1,1)(s_j,t_j) = (1,1),x=11x = 11,cc 减去 11,若 c<0c < 0 结束操作。
    • 若 x=11x = 11,让 (sj,tj)=(0,1)(s_j,t_j) = (0,1),x=10x = 10。
  • 若 (sj,tj)=(0,1)(s_j,t_j) = (0,1),同理 (1,0)(1,0) 的情况。
  • 不可能出现 (sj,tj)=(1,1)(s_j,t_j) = (1,1) 的情况。

注意到 (sj,tj)=(0,0)(s_j,t_j) = (0,0) 的情况可以快速处理,因此把 (0,0)(0,0) 段全部缩起来,用一个栈维护 (sj,tj)≠(0,0)(s_j,t_j) \ne (0,0) 的位置,由于其它操作都将减少 11 的个数,所以总时间复杂度是 O(n)\mathcal O(n) 的。

代码

const int N = 2e6 + 7;
int n, m, k, a[N], b[N], s[N], t[N];
stack<pi> st;

inline void in(int *a, int n) {
	static char s[N];
	rds(s, n);
	for (int i = 1; i <= n; i++) a[n+1-i] = s[i] - '0';
}

inline void out(int *a) {
	int l = max(n, m) + k;
	while (!a[l]) --l;
	for (int i = l; i; i--) printc(a[i] + '0');
	printc('\n');
}

int main() {
	rd(n, m, k), in(a, n), in(b, m);
	for (int i = max(n, m); i; i--) {
		if (a[i] + b[i] == 1) st.push(mp(i, a[i] * 2 + b[i]));
		if (a[i] + b[i] <= 1) continue;
		int c = k, p = i, x = 3;
		stack<pi> sa;
		while (st.size()) {
			pi o = st.top();
			if (p == o.fi) {
				st.pop();
				if ((x ^ o.se) == 3) {
					x = 3;
					if (c) c--;
					else break;
				} else if (x == 3) x &= o.se, sa.push(mp(o.fi, o.se ^ 3));
				p++;
			} else if (x == 3 && o.fi - p <= c) c -= o.fi - p, p = o.fi;
			else break;
		}
		if (x == 3) s[p+c] = t[p+c] = 1;
		else sa.push(mp(p, x));
		while (sa.size()) st.push(sa.top()), sa.pop();
	}
	while (st.size()) {
		pi o = st.top();
		s[o.fi] += o.se >> 1 & 1, t[o.fi] += o.se & 1, st.pop();
	}
	out(s), out(t);
	return 0;
}

标签:二分图、构造、组合计数、贪心

评论

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

    正在连接评论服务…