← 全部文章

AtCoder Grand Contest 020 题解

本文目录(15 节)
  1. Move and Win
  2. Ice Rink Game
  3. Median Sum
  4. Min Max Repetition
  5. 题意
  6. 题解
  7. 代码
  8. Encoding Subsets
  9. 题意
  10. 题解
  11. 代码
  12. Arcs on a Circle
  13. 题意
  14. 题解
  15. 代码

AtCoder Grand Contest 020

Move and Win

答案只与 a,ba,b 是否同奇偶有关。

int main() {
	int n, a, b;
	rd(n, a, b);
	prints((a ^ b) & 1 ? "Borys" : "Alice");
	return 0;
}

Ice Rink Game

倒序考虑即可。

const int N = 1e5 + 7;
int n;
ll a[N], l = 2, r = 2;

int main() {
	rd(n), rda(a, n);
	for (int i = n; i; i--) {
		l = ((l - 1) / a[i] + 1) * a[i], r = r / a[i] * a[i];
		if (l > r) return print(-1), 0;
		r += a[i] - 1;
	}
	print(l, r);
	return 0;
}

Median Sum

设所有元素之和为 ss。

考虑每个非空非全的子集与它的补集之和都等于 ss,加上全集 ss 后的中位数,就是最小的 ≥⌈s2⌉\ge \lceil \frac s2 \rceil 且存在的子集和。

bitset 优化背包时间复杂度 O(n3w)\mathcal O(\frac{n^3}{w})。

const int N = 2e3 + 7;
int n, a[N], s;
bitset<N*N> b;

int main() {
	rd(n), rda(a, n), b[0] = 1;
	for (int i = 1; i <= n; i++) b |= b << a[i], s += a[i];
	s = (s + 1) >> 1;
	while (!b[s]) ++s;
	print(s);
	return 0;
}

Min Max Repetition

题意

  • 设 a,ba,b 为正整数,定义 f(a,b)f(a,b) 表示满足下列条件的字符串:
    • f(a,b)f(a,b) 由恰好 aa 个 A 和恰好 bb 个 B 构成。
    • f(a,b)f(a,b) 最长的字符全相同的子串是满足上述条件的所有字符串中最短的。
    • f(a,b)f(a,b) 是满足上述条件的所有字符串中字典序最小的。
  • 给定 c,dc,d,求 f(a,b)[c:d]f(a,b)[c:d]。
  • a,b≤5×108a,b \le 5 \times 10^8,d−c+1≤100d-c+1 \le 100,多组数据组数 T≤103T\le 10^3。

题解

最长的字符全相同的子串的长度为 k=max⁡(⌈ab+1⌉,⌈ba+1⌉)k = \max(\lceil\frac{a}{b+1}\rceil,\lceil\frac{b}{a+1}\rceil)。

贪心的构造串可以发现答案为 (AkB)∞(\texttt{A}^k\texttt{B})^{\infty} 的某个前缀加上 (ABk)∞(\texttt{A}\texttt{B}^k)^{\infty} 的某个后缀,二分出分界点即可,时间复杂度 O(T(log⁡(a+b)+(d−c)))\mathcal O(T(\log(a+b) + (d-c)))。

代码

int a, b, c, d, k;

inline bool pd(int o) {
	if (!o) return 1;
	if (!(o % (k + 1))) return pd(o - 1);
	int x = a - (o - o / (k + 1)), y = b - o / (k + 1);
	return y <= 1ll * (x + 1) * k;
}

inline void solve() {
	rd(a, b), rd(c, d);
	k = (max(a, b) - 1) / (min(a, b) + 1) + 1;
	int l = 0, r = a + b;
	while (l < r) {
		int mid = (l + r + 1) >> 1;
		if (pd(mid)) l = mid;
		else r = mid - 1;
	}
	for (int i = c; i <= min(l, d); i++)
		printc(i % (k + 1) ? 'A' : 'B');
	for (int i = max(c, l + 1); i <= d; i++)
		printc((a + b - i + 1) % (k + 1) ? 'B' : 'A');
	prints("");
}

int main() {
	int T;
	rd(T);
	while (T--) solve();
	return 0;
}

Encoding Subsets

题意

  • 对于一个 0/10/1 串,我们可以将其按如下方式编码:
    • 0/10/1 串 00 可以被编码为 0\texttt{0},0/10/1 串 11 可以被编码为 1\texttt{1}。
    • 若 0/10/1 串 AA 可以被编码为 PP,0/10/1 串 BB 可以被编码为 QQ,则 0/10/1 串 ABAB 可以被编码为 PQPQ。
    • 若 0/10/1 串 AA 可以被编码为 PP,对于 k≥2k \ge 2,0/10/1 串 AkA^k 可以被编码为 (Pxk)\texttt{(}P\texttt{x}k\texttt{)}。
  • 显然一个 0/10/1 串可能有多种编码方案。
  • 定义 0/10/1 串 AA 是 0/10/1 串 BB 的子集,当且仅当 AA 和 BB 长度,且对于所有 Ai=1A_i = 1 的 ii 满足 Bi=1B_i = 1。
  • 给定 0/10/1 串 SS,求出所有 SS 的子集的编码方案数之和。
  • ∣S∣≤100|S| \le 100,答案对 998244353998244353 取模。

题解

考虑对于一个固定的 0/10/1 串如何求解方案数,考虑区间 DP,设 fl,rf_{l,r} 表示将区间 [l,r][l,r] 编码的方案数,gl,rg_{l,r} 表示将区间 [l,r][l,r] 编码成单个字符或由一个括号括起来(允许嵌套)的方案数,则转移有: fl,r=∑k=lr−1gl,kfk+1,r f_{l,r}=\sum_{k=l}^{r-1}g_{l,k} f_{k+1,r}

gl,r=∑d∣r−l+1[d为[l,r]的循环节]fl,l+d−1 g_{l,r}=\sum_{d|r-l+1} [d为[l,r]的循环节]f_{l,l+d-1}

考虑原问题,直接拿字符串来定义状态,在 gg 的转移中枚举 dd 后对划分出的子串取并进行转移即可。

由于状态数并不多,时间复杂度 O(能过)\mathcal O(能过)。

代码

map<string, modint> f, g;

modint F(string s);

modint G(string s) {
	if (g.find(s) != g.end()) return g[s];
	int n = s.size();
	modint ans;
	for (int d = 1; d < n; d++) {
		if (n % d) continue;
		string t = "";
		for (int i = 0; i < d; i++) {
			bool c = 1;
			for (int j = i; j < n; j += d) c &= s[j] - '0';
			t += c + '0';
		}
		ans += F(t);
	}
	return g[s] = ans;
}

modint F(string s) {
	if (f.find(s) != f.end()) return f[s];
	int n = s.size();
	modint ans;
	for (int i = 1; i <= n; i++)
		ans += G(s.substr(0, i)) * F(s.substr(i, n));
	return f[s] = ans;
}

int main() {
	string s;
	rds(s);
	f[""] = 1, g[""] = 1, g["0"] = 1, g["1"] = 2;
	print(F(s));
	return 0;
}

Arcs on a Circle

题意

  • 有一个周长为 cc 的圆。
  • 你有 nn 条弧,第 ii 条弧的弧长为 lil_i。
  • 对于每条弧,你会在圆上随机一个位置作为它的中点放置。
  • 求圆上每个点都被至少一条弧覆盖的概率。
  • n≤6n \le 6,c≤50c \le 50。

题解

考虑断环为链,以最长弧的一端为端点断开这个环。

由于所有的弧长都是整数,所以我们不关心所有弧起始点的小数部分,我们只关心小数部分之间的大小关系。

我们可以暴力枚举这个大小关系,于是问题变成:在区间 [0,nc)[0,nc) 上,有一条已知长度的从 00 开始的线段,另外还有 n−1n-1 条已知长度,且起点  mod n\bmod n 确定的线段,要求这些线段覆盖整个区间的方案数。

考虑状压 DP,设 fi,j,sf_{i,j,s} 表示当前在点 ii,已经覆盖到了点 jj,使用的线段集合为 ss 的方案数,转移时考虑 i+1i+1 是否为某个线段的起点即可。

由于我们一开始选择的是最长的线段,因此因为断开了环而断开的线段在开头处不会有贡献,可以直接忽略掉。

时间复杂度 O((n−1)!n2c22n−1)\mathcal O((n-1)!n^2c^22^{n-1})。

代码

int n, m, k, l[7], p[7], f[307][307][135];

inline int work() {
	memset(f, 0, sizeof(f));
	f[0][l[n]*n][0] = 1;
	for (int i = 1; i < n * m; i++)
		for (int j = i; j <= n * m; j++)
			for (int s = 0; s <= k; s++)
				if (f[i-1][j][s]) {
					f[i][j][s] += f[i-1][j][s];
					int x = i % n;
					if (!x || s >> (x - 1) & 1) continue;
					f[i][min(n*m,max(j,i+l[p[x]]*n))][s|1<<(x-1)] += f[i-1][j][s];
				}
	return f[n*m-1][n*m][k];
}

int main() {
	rd(n, m), rda(l, n), k = (1 << (n - 1)) - 1;
	sort(l + 1, l + 1 + n), iota(p + 1, p + n, 1);
	ll ans = 0, cnt = 1;
	for (int i = 1; i < n; i++) cnt *= i * m;
	do ans += work();
	while (next_permutation(p + 1, p + n));
	printf("%.20Lf\n", 1.0L * ans / cnt);
	return 0;
}

标签:bitset、DP、二分、区间 DP、博弈论、数论、状压、组合计数、背包、记忆化搜索、贪心

评论

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

    正在连接评论服务…