← 全部文章

AtCoder Grand Contest 046 题解

本文目录(6 节)
  1. Takahashikun, The Strider
  2. Extension
  3. Shift
  4. Secret Passage
  5. Permutation Cover
  6. Forbidden Tournament

AtCoder Grand Contest 046

Takahashikun, The Strider

相当于要找到最小的正整数 tt 满足 tx≡0(mod360)tx \equiv 0 \pmod {360},暴力即可。

int main() {
	int x;
	rd(x);
	int t = 1;
	while (1) {
		if (t * x % 360 == 0) {
			return print(t), 0;
		}
		++t;
	}
	return 0;
}

Extension

O(n2)\mathcal O(n^2) DP,转移时容斥一下。

const int N = 3e3 + 7;
int a, b, c, d;
modint f[N][N];

int main() {
	rd(a, b), rd(c, d);
	f[a][b] = 1;
	for (int i = a; i <= c; i++)
		for (int j = b; j <= d; j++) {
			if (i == a && j == b) continue;
			f[i][j] = f[i-1][j] * j + f[i][j-1] * i - f[i-1][j-1] * (i - 1) * (j - 1);
		}
	print(f[c][d]);
	return 0;
}

Shift

设有 tt 个 00,相当于用这 tt 个 00 将 11 分成 t+1t+1 堆,然后可以进行 kk 次操作,每次操作把一个 11 移到左边某一堆中,求最终的方案数。

考虑 DP,设 fi,j,lf_{i,j,l} 考虑前 ii 堆,已经使用了 kk 次中的 jj 次,前 ii 堆中多了 ll 个 11 的方案数。

转移时枚举第 i+1i+1 堆的变化量,时间复杂度 O(n4)\mathcal O(n^4),常数很小,注意特判 t=0t=0 的情况。

const int N = 307;
int n, k, c, t, x, p[N];
char s[N];
modint f[N][N][N], ans;

int main() {
	rds(s, n), rd(k);
	for (int i = 1; i <= n; i++) c += s[i] == '1';
	if (c == n) return print(1), 0;
	for (int i = 1; i <= n; i++)
		if (s[i] == '0') {
			++t;
			int j = i;
			while (s[j-1] == '1') --j, ++p[t];
			x = i;
		}
	p[++t] = n - x;
	k = min(k, c - p[1]);
	for (int j = p[1]; j <= min(c, k + p[1]); j++)
		f[1][j-p[1]][j-p[1]] = 1;
	for (int i = 1; i < t - 1; i++)
		for (int j = 0; j <= k; j++)
			for (int l = 0; l <= j; l++)
				if (f[i][j][l].x)
					for (int w = -min(p[i+1], l); w <= k - j; w++)
						f[i+1][j+max(0,w)][l+w] += f[i][j][l];
	for (int j = 0; j <= k; j++)
		for (int l = 0; l <= min(j, p[t]); l++)
			ans += f[t-1][j][l];
	print(ans);
	return 0;
}

Secret Passage

设 fi,j,kf_{i,j,k} 表示从 s1…is_{1\dots i} 中抽出 jj 个 00 和 kk 个 11 插入到 si+1…ns_{i+1\dots n} 中的方案数。

注意我们暂时先不考虑 s1…is_{1\dots i} 中能否抽出 jj 个 00 和 kk 个 11,只进行计数。

这个 DP 可以 O(1)\mathcal O(1) 转移,时间复杂度 O(n3)\mathcal O(n^3)。

接下来的问题就是判断 s1…is_{1\dots i} 中能否抽出 jj 个 00 和 kk 个 11,如果可以抽出,则将答案加上 fi,j,kf_{i,j,k}。

注意我们每次操作可以将一个字符插入到 si+1…ns_{i+1\dots n} 中,也可以将其依旧放在前面。

如果依旧放在前面,若后面依然需要把这个字符插入到 si+1…ns_{i+1 \dots n},那一定比不放在前面直接插入要劣,因此我们不妨要求依旧放在前面的字符唯一的用处是,删除这个字符,以让其他字符能够插入到后面。

于是设 gi,j,kg_{i,j,k} 表示考虑 s1…is_{1\dots i} 已经抽出了 jj 和 00 和 kk 个 11 时,最多还剩下多少个可以被删除的字符。

这个 DP 同样可以 O(1)\mathcal O(1) 转移,时间复杂度 O(n3)\mathcal O(n^3)。

最终 gi,j,k≥0g_{i,j,k} \ge 0 的位置,以及其衍生出更劣的位置都是合法的位置。

总时间复杂度 O(n3)\mathcal O(n^3),注意最后需要减去空串。

const int N = 307;
int n;
char s[N];
modint f[N][N][N], ans;
int g[N][N][N];
bool v[N][N][N];

inline void upd(int &x, int y) {
	x = max(x, y);
}

int main() {
	rds(s, n);
	f[n][0][0] = 1;
	for (int i = n; i; i--)
		for (int j = 0; j < i; j++)
			for (int k = 0; j + k < i; k++) {
				f[i-1][j][k] += f[i][j][k];
				if (s[i] == '0') f[i][j][k+1] += f[i][j][k];
				else f[i][j+1][k] += f[i][j][k];
			}
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= n; j++)
			for (int k = 0; k <= n; k++)
				g[i][j][k] = -1;
	g[0][0][0] = 0;
	for (int i = 0; i < n; i++)
		for (int j = 0; j <= (i >> 1); j++)
			for (int k = 0; j + k <= (i >> 1); k++)
				if (~g[i][j][k]) {
					if (s[i+1] == '0') upd(g[i+1][j+1][k], g[i][j][k] - 1);
					else upd(g[i+1][j][k+1], g[i][j][k] - 1);
					if (i + 2 <= n) {
						upd(g[i+2][j][k], g[i][j][k] + 1);
						if (s[i+1] == '0' || s[i+2] == '0')
							upd(g[i+2][j+1][k], g[i][j][k]);
						if (s[i+1] == '1' || s[i+2] == '1')
							upd(g[i+2][j][k+1], g[i][j][k]);
					}
				}
	for (int i = 0; i <= n; i++)
		for (int j = n; ~j; j--)
			for (int k = n; ~k; k--) {
				v[i][j][k] = ~g[i][j][k];
				if (i) v[i][j][k] |= v[i-1][j][k];
				v[i][j][k] |= v[i][j+1][k];
				v[i][j][k] |= v[i][j][k+1];
			}
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= i; j++)
			for (int k = 0; j + k <= i; k++)
				if (v[i][j][k]) ans += f[i][j][k];
	print(ans - 1);
	return 0;
}

Permutation Cover

显然有解的充要条件为 max⁡ai≤2⋅min⁡ai\max a_i \le 2 \cdot \min a_i。

看见字典序最小,显然贪心枚举,不同之处在考虑每次加入一段排列(可能与之前的重合),问题转化成一个前缀是否合法。

设此时的前缀为 SS,显然最后 kk 个为一个排列,设为 pp,再设 ii 还要加入 bib_i 个。

此时跟一开始判定唯一的区别为 max⁡bi=2⋅min⁡bi+1\max b_i = 2 \cdot \min b_i + 1 时仍然可能有解,此时附加的充要条件为,设 jj 满足 bj=max⁡bib_j = \max b_i,kk 满足 bk=min⁡bib_k = \min b_i,则 jj 要在 kk 之前。

时间复杂度 O(nk2)\mathcal O(nk^2)。

const int N = 1e3 + 7;
int n, m, s, a[N], p[N], q[N], ans[N];

void work(int k) {
	bool ok1 = 1, ok2 = 0;
	for (int i = 1; i <= k; i++) --a[p[i]];
	int mn = *min_element(a + 1, a + n + 1);
	int mx = *max_element(a + 1, a + n + 1);
	if (mn * 2 >= mx) {
		ok2 = 1;
		copy(p + 1, p + k + 1, q + 1);
		sort(q + 1, q + k + 1);
	} else if (mn * 2 + 1 == mx) {
		int k1 = 0, k2 = 0;
		static int f1[N], f2[N];
		for (int i = 1; i <= k; i++)
			((a[p[i]] == mn || a[p[i]] == mx) ? f1[++k1] : f2[++k2]) = p[i];
		sort(f1 + 1, f1 + k1 + 1, [&](int i, int j) {
			if ((a[i] == mn) == (a[j] == mn)) return i < j;
			return (a[i] == mn) < (a[j] == mn);
		});
		sort(f2 + 1, f2 + k2 + 1);
		int x1 = 1, x2 = 1;
		for (int i = 1; i <= k; i++)
			if (x1 <= k1 && x2 <= k2)
				q[i] = f1[x1] < f2[x2] ? f1[x1++] : f2[x2++];
			else q[i] = x1 <= k1 ? f1[x1++] : f2[x2++];
		ok2 = 1;
		for (int i = k + 1; i <= n; i++)
			if (a[p[i]] == mn) ok2 = 0;
		ok2 |= !x1 || (a[f1[1]] == mx);
	}
	for (int i = 1; i <= k; i++) ++a[p[i]];
	if (!ok1 || !ok2) q[1] = 0;
}

inline bool pd(int *a, int *b) {
	int p = 1;
	while (a[p] == b[p]) ++p;
	return a[p] < b[p];
}

int main() {
	rd(n), rda(a, n);
	for (int i = 1; i <= n; i++) s += a[i];
	int mn = *min_element(a + 1, a + n + 1);
	int mx = *max_element(a + 1, a + n + 1);
	if (mn * 2 < mx) return print(-1), 0;
	iota(p + 1, p + n + 1, 1), work(n);
	for (int i = 1; i <= n; i++) --a[q[i]], ans[++m] = p[i] = q[i];
	while (m != s) {
		static int t[N];
		memset(t, 0, sizeof(t)), t[1] = n + 1;
		int k = 0;
		for (int i = 1; i <= n; i++) {
			work(i);
			if (q[1] && pd(q, t))
				k = i, copy(q + 1, q + i + 1, t + 1);
		}
		for (int i = 1; i + k <= n; i++) p[i] = p[i+k];
		for (int i = 1; i <= k; i++)
			ans[++m] = p[n-k+i] = t[i], --a[t[i]];
	}
	printa(ans, m);
	return 0;
}

Forbidden Tournament

咕.

标签:DP、容斥、数论、组合计数、贪心

评论

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

    正在连接评论服务…