← 全部文章

AtCoder Grand Contest 041 题解

本文目录(6 节)
  1. Table Tennis Training
  2. Voting Judges
  3. Domino Quality
  4. Problem Scores
  5. Balancing Network
  6. Histogram Rooks

AtCoder Grand Contest 041

Table Tennis Training

如果 a,ba,b 奇偶性相同,那么一起往中间走即可。

否则,走到某一端,浪费一次步数之后再一起往中间走。

int main() {
	ll n, a, b;
	rd(n, a, b);
	if ((a ^ b) & 1) print(min((a + b - 1) >> 1, (n - a + n - b + 1) >> 1));
	else print((b - a) >> 1); 
	return 0;
}

Voting Judges

贪心,考虑清楚合法条件,需要前缀和一下。

const int N = 1e5 + 7;
int n, m, v, p, a[N];
ll s[N];
 
int main() {
	rd(n, m), rd(v, p), rda(a, n);
	sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; i++) s[i] = a[i] + s[i-1];
	for (int i = 1; i <= n; i++) {
		int c = n + 1 - (upper_bound(a + 1, a + n + 1, a[i] + m) - a);
		if (c + 1 > p) continue;
		int d = (upper_bound(a + 1, a + n + 1, a[i]) - a) - 2;
		ll o = 1ll * m * (v - 1 - c - d) - (1ll * (a[i] + m) * (n - 1 - c - d) - (s[n-c] - s[d+1]));
		if (o <= (s[n-c] - s[n-p+1]) - 1ll * (p - 1 - c) * a[i]) {
			print(n - i + 1);
			break;
		}
	}
	return 0;
}

Domino Quality

n≤2n \le 2 无解,手玩出 n=3/4/5/6/7n=3/4/5/6/7,用 4/5/6/74/5/6/7 去凑出后面的数。

const string S4[4] = {"abcc", "abdd", "ccab", "ddab"};
const string S5[5] = {"abbcc", "ad..a", "bd..a", "b.eez", "oorrz"};
const string S6[6] = {"oorrzz", ".a.b.c", ".a.b.c", "d.e.f.", "d.e.f.", "oorrzz"};
const string S7[7] = {".oorrzz", "oabb...", "oa.c...", "rddc...", "r...abb", "z...a.c", "z...ddc"};

const int N = 1e3 + 7;
int n;
char s[N][N];

inline void solve(int &n, int x) {
	if (x == 4)
		for (int i = 0; i < x; i++)
			for (int j = 0; j < x; j++)
				s[n+i][n+j] = S4[i][j];
	if (x == 5)
		for (int i = 0; i < x; i++)
			for (int j = 0; j < x; j++)
				s[n+i][n+j] = S5[i][j];
	if (x == 6)
		for (int i = 0; i < x; i++)
			for (int j = 0; j < x; j++)
				s[n+i][n+j] = S6[i][j];
	if (x == 7)
		for (int i = 0; i < x; i++)
			for (int j = 0; j < x; j++)
				s[n+i][n+j] = S7[i][j];
	n += x;
}

int main() {
	rd(n);
	if (n <= 2) return print(-1), 0;
	if (n == 3) return prints("a.."), prints("a.."), prints(".bb"), 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			s[i][j] = '.';
	int m = 1;
	solve(m, n % 4 + 4);
	while (m <= n) solve(m, 4);
	for (int i = 1; i <= n; i++) prints(s[i], n);
	return 0;
}

Problem Scores

限制相当于要求前 k+1k+1 个数的和大于后 kk 个数的和。

注意到如果 k>⌊n−12⌋k > \lfloor \frac {n-1}2 \rfloor 会有重复的部分,把重复的部分去掉之后就变成 k≤⌊n−12⌋k \le \lfloor \frac {n-1}2 \rfloor 的情况,因此我们只用考虑 k≤⌊n−12⌋k \le \lfloor \frac {n-1}2 \rfloor 的情况。

又当 k≤⌊n−12⌋k \le \lfloor \frac {n-1}2 \rfloor 时,若 kk 满足条件,则 k−1k-1 一定满足条件,因此我们只用考虑 k=⌊n−12⌋k = \lfloor \frac {n-1}2 \rfloor 的情况。


先考虑 nn 为偶数的情况,此时有且仅有 ak+2a_{k+2} 既不在前 k+1k+1 个数中,又不在后 kk 个数中。

考虑枚举 ak+2=xa_{k+2} = x,先将所有数都设为 xx,此时前 k+1k+1 个数的和比后 kk 个数的和大 xx。

然后我们可以做的就是让前 k+1k+1 个数的若干个前缀同时 −1-1,后 kk 个数的若干个后缀同时 +1+1,使得:

  • 前 k+1k+1 个数最多进行 x−1x-1 次 −1-1。
  • 后 kk 个数最多进行 n−xn-x 次 +1+1。
  • 前 k+1k+1 个数减掉的总数与后 kk 个数加上的总数之和 <x< x。

设 fj,lf_{j,l} 表示前 k+1k+1 个数进行了 jj 次 −1-1,共减掉 ll 的方案数,gj,lg_{j,l} 表示后 kk 个数进行了 jj 次 +1+1,共加上 ll 的方案数。

则答案为: ∑x=1n∑p=0x−1∑q=0n−x∑i=0x−1∑j=0x−1−ifp,igq,j \sum_{x=1}^n \sum_{p=0}^{x-1} \sum_{q=0}^{n-x} \sum_{i=0}^{x-1} \sum_{j=0}^{x-1-i} f_{p,i} g_{q,j} 这玩意儿在求出 f,gf,g 之后借助前缀和可以做到 O(n2)\mathcal O(n^2)。

由于 f,gf,g 定义类似,这里只考虑如何求 ff。

在 [1,k+1][1,k+1] 的范围内从大到小枚举 ii,表示此时 −1-1 会导致总共 −i-i,再枚举一个状态 fj,lf_{j,l},转移到 fj+1,l+if_{j+1,l+i}。

乍看之下复杂度为 O(n3)\mathcal O(n^3),但仔细观察可以发现 ll 只需要从 ijij 开始枚举即可,又 ij<nij < n,因此有效的 i,ji,j 只有 O(nlog⁡n)\mathcal O(n \log n) 种,总时间复杂度 O(n2log⁡n)\mathcal O(n^2 \log n)。


最后是当 nn 为奇数的情况,唯一的区别就是变成枚举 ak+1a_{k+1},其它的都很类似,事实上只需要将求 ff 时枚举 ii 的范围从 [1,k+1][1,k+1] 改成 [1,k][1,k] 即可。

const int N = 5e3 + 7;
int n, k;
modint f[N][N], g[N][N], ans;

int main() {
	rd(n, P), k = (n - 1) >> 1;
	f[0][0] = g[0][0] = 1;
	for (int i = k + !(n & 1); i; i--)
		for (int j = 0; i * (j + 1) < n; j++)
			for (int l = i * j; l + i < n; l++)
				f[j+1][l+i] += f[j][l];
	for (int i = k; i; i--)
		for (int j = 0; i * (j + 1) < n; j++)
			for (int l = i * j; l + i < n; l++)
				g[j+1][l+i] += g[j][l];
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			f[i][j] += (i ? f[i-1][j] : 0),
			g[i][j] += (i ? g[i-1][j] : 0) + (j ? g[i][j-1] : 0) - ((i && j) ? g[i-1][j-1] : 0);
	for (int x = 1; x <= n; x++)
		for (int i = 0; i < x; i++)
			ans += f[x-1][i] * g[n-x][x-1-i];
	print(ans);
	return 0;
}

Balancing Network

对于 T=1T = 1,设 ai,ja_{i,j} 表示 jj 是否能到达 ii,则依次考虑每个操作 (x,y)(x,y),相当于将 axa_x 和 aya_y 取并,bitset 即可,时间复杂度 O(nmw)\mathcal O(\frac {nm}w)。

对于 T=2T = 2,注意到 n=3n=3 时必然有解,于是怎么搞都行了。

const int N = 5e4 + 7, M = 1e5 + 7;
int n, m, t, x[M], y[M];
bitset<N> a[N];
bool v[N];
int b[N], c[N];
char s[M];

int main() {
	rd(n, m, t);
	if (t == 1) {
		for (int i = 1; i <= n; i++) a[i][i] = 1;
		for (int i = 1; i <= m; i++)
			rd(x[i], y[i]), a[x[i]] = a[y[i]] |= a[x[i]];
		for (int i = 1; i <= n; i++)
			if ((int)a[i].count() == n) {
				v[i] = 1;
				for (int i = m; i; i--)
					if (v[x[i]]) s[i] = '^', v[y[i]] = 1;
					else if (v[y[i]]) s[i] = 'v', v[x[i]] = 1;
					else s[i] = '^';
				prints(s, m);
				return 0;
			}
		print(-1);
	} else {
		if (n == 2) return print(-1), 0;
		for (int i = 1; i <= n; i++) b[i] = i, c[i] = 1;
		for (int i = 1; i <= m; i++) rd(x[i], y[i]);
		for (int i = m; i; i--)
			if (b[x[i]] == b[y[i]]) s[i] = '^';
			else {
				if (c[b[x[i]]] <= 1) swap(x[i], y[i]), s[i] = '^';
				else s[i] = 'v';
				--c[b[x[i]]], b[x[i]] = b[y[i]], ++c[b[x[i]]];
			}
		prints(s, m);
	}
	return 0;
}

Histogram Rooks

咕

标签:bitset、DP、二分、前缀和/差分、构造、组合计数、调和级数、贪心

评论

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

    正在连接评论服务…