Codeforces Global Round 26 (A - E)
Codeforces Global Round 26
A
如果 \(a_1 = a_n\),无解。
如果 \(a_2 = a_n\),\(a_1, a_2\) 涂成红色,否则只把 \(a_1\) 涂成红色。
void solve() {
cin >> n;
for(int i = 1; i <= n; ++ i) cin >> a[i];
if(a[1] == a[n]) {
cout << "NO\n";
return;
}
cout << "YES\n";
if(a[2] != a[n]) {
cout << "R";
for(int i = 2; i <= n; ++ i) {
cout << "B";
}
cout << '\n';
return;
}
cout << "RR";
for(int i = 3; i <= n; ++ i) {
cout << "B";
}
cout << '\n';
}
B
如果首位不等于 \(1\),无解。
否则可以推出两个数 \(a + b = x\) 每一位的和,且这个和一定属于 \([10, 18]\),否则无解。
例如:\(1393938\)。
- \(a_0 + b_0 = 18\)。
- \(a_1 + b_1 = 13 - 1\)(一定有进位)。
- \(a_2 + b_2 = 19 - 1\)。
以此类推。
void solve() {
ll x; cin >> x;
auto s = to_string(x);
int n = s.length();
if(s[0] != '1') {
cout << "NO\n";
return;
}
for(int i = 0; i <= n - 2; ++ i) {
int x = 10 + s[i + 1] - '0';
if(i != n - 2) -- x;
if(x < 10 || x > 18) {
cout << "NO\n";
return;
}
}
cout << "YES\n";
}
C1
最多只会用一次操作二。
反证法,考虑最后两次操作二分别对应 \(c + a_i\) 和 \(c + a_j\)。显然 \(c + a < 0\) ,否则没必要用操作二。如果 \(i\) 处用了操作 \(1\) ,\(c + a_j\) 将变得更小,从而使答案增大。
维护最小前缀和 \(x = \min(s_i, 0)\),则最终的 \(c = s_n - 2x\)。
void solve() {
ll n, mi = 0, s = 0;
cin >> n;
for(int i = 1; i <= n; ++ i) {
int x; cin >> x;
mi = min(mi, s += x);
}
cout << s - mi * 2 << '\n';
}
C2
延续 C1 的思考方向。
如果 \(x = 0\),操作中不出现负数。因此操作一操作二是等效的,方案数为 \(2^n\)。
如果 \(s_i = x\),说明可以在这一步进行操作二达到最终的 \(c\)。
记 \(k = \sum\limits_{j <i}[s_j\ge0]\),表示在 \(i\) 之前有 \(k\) 步可以用两种操作。因为 \(c \ge 0\),所以在 \(i\) 之后的所有的操作都是非负的,贡献为 \(2^k \times 2^{n - i}\)。
void solve() {
cin >> n;
mi = 0;
for(int i = 1; i <= n; ++ i) {
cin >> s[i];
mi = min(mi, s[i] += s[i - 1]);
}
if(mi == 0) {
cout << p[n] << '\n';
return;
}
ll ans = 0;
for(int i = 1, k = 0; i <= n; ++ i) {
if(s[i] >= 0) ++ k;
if(s[i] == mi) {
ans = (ans + p[k] * p[n - i]) % P;
}
}
cout << ans << '\n';
}
D
设 \(s\) 长度为 \(n\)。特判全为 a
的情况,共 \(n - 1\) 种方案。
设 \(s\) 中非 a
字符的数量为 \(m\)。
如果 \(t\) 中有 \(k\) 个非 a
字符,把 \(t\) 掐头去尾忽略前后的 a
后,原串里一定恰好存在 \(\dfrac{m}{k}\) 个 \(t'\),且 \(k\mid m\)。
枚举 \(k\)。记录第 \(i\) 个非 a
字符的位置为 \(p_i\)(从 \(0\) 开始),如果 \(s_{p_i} \ne s_{p_i - k}\) 或不在分割处的 \(p_i - p_{i - 1}\ne p_{i - k} - p_{i - k - 1}\),\(t'\) 不合法。
把 \(t'\) 还原为 \(t\),枚举左边有的 a
个数 \(l \in [0, p_0]\)。
设 \(x\) 为分割处的最小间隔 \(\min(p_i - p_{i - 1}),\ k \mid i\),则右边的 a
个数 \(r \in [0, \min(x - l, n - p_{m - 1} - 1)]\)。
void solve() {
string s;
cin >> s;
int n = s.length();
if(count(s.begin(), s.end(), 'a') == n) {
cout << n - 1 << '\n';
return;
}
vector<int> a;
for(int i = 0; i < n; ++ i) {
if(s[i] != 'a') {
a.eb(i);
}
}
int m = a.size();
ll ans = 0;
for(int i = 1; i <= m; ++ i) {
if(m % i) {
continue;
}
int ok = 1;
for(int j = i; j < m; ++ j) {
int o = j % i;
if(s[a[j]] != s[a[o]] || (o && a[o] - a[o - 1] != a[j] - a[j - 1])) {
ok = 0;
break;
}
}
if(ok) {
int mi = n;
for(int j = i; j < m; j += i) {
mi = min(mi, a[j] - a[j - 1] - 1);
}
int r = n - a.back() - 1;
for(int l = 0; l <= a[0]; ++ l) {
ans += max(0, min(r + 1, mi - l + 1));
}
}
}
cout << ans << '\n';
}
E
把一个点的父亲儿子都加到 \(T_2\) 后,这个点就是叶子,且与之直接相连的点都不是叶子。(所有相邻点都是这个点的祖先)。
所有的叶子构成一个独立集,我们找去除根后最大的,类似 上司的舞会。
枚举每个节点作为初始的根,换根 dp。如果根本身就是叶子还要在加一。
具体来说,第一次扫描以 \(1\) 为根求出 \(f_{x, 1/0}\) 表示以 \(x\) 为根的子树中 \(x\) 选/不选的最大独立集:\(\begin{cases}f_{x, 0} = \sum_{y \in g_x} \max(f_{y, 0}, f_{y, 1}) \\ f_{x, 1} = \sum_{y \in g_x} f_{y, 0}\end{cases}\)。
第二次扫描求出 \(h_{x, 1/0}\) 表示选/不选 \(x\) 的最大独立集,\(h_{1, 0/1} = f_{1, 0/1},\ \begin{cases}h_{x, 0} = \max(h_{fa, 1},\ f_{x, 0} + (h_{fa, 0} - \max(f_{x, 1}, f_{x, 0})))\\h_{x, 1} = f_{x, 1} + (h_{fa, 0} - \max(f_{x, 1}, f_{x, 0})) \end{cases}\)。
用 \(h_{x, 0} + [x \texttt{ is leaf}]\) 更新答案。
void solve() {
int n;
cin >> n;
vector<vector<int>> g(n + 1);
for(int i = 1; i < n; ++ i) {
int x, y;
cin >> x >> y;
g[x].eb(y);
g[y].eb(x);
}
vector<array<int, 2>> f(n + 1, {0, 0});
auto dfs = [&](auto &&dfs, int x, int fa) -> void {
f[x][0] = 0;
f[x][1] = 1;
for(int y : g[x]) {
if(y != fa) {
dfs(dfs, y, x);
f[x][0] += max(f[y][0], f[y][1]);
f[x][1] += f[y][0];
}
}
};
dfs(dfs, 1, 0);
vector<array<int, 2>> h(n + 1);
h[1][0] = f[1][0];
h[1][1] = f[1][1];
int ans = h[1][0] + (g[1].size() == 1);
auto dfs2 = [&](auto &&dfs2, int x, int fa) -> void {
int tmp = h[fa][0] - max(f[x][0], f[x][1]);
h[x][1] = f[x][1] + tmp;
h[x][0] = max(h[fa][1], f[x][0] + tmp);
tmp = h[x][0] + (g[x].size() == 1);
ans = max(ans, tmp);
for(int y : g[x]) {
if(y != fa) {
dfs2(dfs2, y, x);
}
}
};
for(int y : g[1]) {
dfs2(dfs2, y, 1);
}
cout << ans << '\n';
}