AtCoder Grand Contest 020 题解
本文目录(15 节)
Move and Win
答案只与 是否同奇偶有关。
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
设所有元素之和为 。
考虑每个非空非全的子集与它的补集之和都等于 ,加上全集 后的中位数,就是最小的 且存在的子集和。
bitset 优化背包时间复杂度 。
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和恰好 个B构成。 - 最长的字符全相同的子串是满足上述条件的所有字符串中最短的。
- 是满足上述条件的所有字符串中字典序最小的。
- 由恰好 个
- 给定 ,求 。
- ,,多组数据组数 。
题解
最长的字符全相同的子串的长度为 。
贪心的构造串可以发现答案为 的某个前缀加上 的某个后缀,二分出分界点即可,时间复杂度 。
代码
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
题意
- 对于一个 串,我们可以将其按如下方式编码:
- 串 可以被编码为 , 串 可以被编码为 。
- 若 串 可以被编码为 , 串 可以被编码为 ,则 串 可以被编码为 。
- 若 串 可以被编码为 ,对于 , 串 可以被编码为 。
- 显然一个 串可能有多种编码方案。
- 定义 串 是 串 的子集,当且仅当 和 长度,且对于所有 的 满足 。
- 给定 串 ,求出所有 的子集的编码方案数之和。
- ,答案对 取模。
题解
考虑对于一个固定的 串如何求解方案数,考虑区间 DP,设 表示将区间 编码的方案数, 表示将区间 编码成单个字符或由一个括号括起来(允许嵌套)的方案数,则转移有:
考虑原问题,直接拿字符串来定义状态,在 的转移中枚举 后对划分出的子串取并进行转移即可。
由于状态数并不多,时间复杂度 。
代码
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
题意
- 有一个周长为 的圆。
- 你有 条弧,第 条弧的弧长为 。
- 对于每条弧,你会在圆上随机一个位置作为它的中点放置。
- 求圆上每个点都被至少一条弧覆盖的概率。
- ,。
题解
考虑断环为链,以最长弧的一端为端点断开这个环。
由于所有的弧长都是整数,所以我们不关心所有弧起始点的小数部分,我们只关心小数部分之间的大小关系。
我们可以暴力枚举这个大小关系,于是问题变成:在区间 上,有一条已知长度的从 开始的线段,另外还有 条已知长度,且起点 确定的线段,要求这些线段覆盖整个区间的方案数。
考虑状压 DP,设 表示当前在点 ,已经覆盖到了点 ,使用的线段集合为 的方案数,转移时考虑 是否为某个线段的起点即可。
由于我们一开始选择的是最长的线段,因此因为断开了环而断开的线段在开头处不会有贡献,可以直接忽略掉。
时间复杂度 。
代码
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;
}
评论
暂无已公开评论。