Atcoder Beginner Contest 447 实况记录 + 题解
A
语法题。
B
语法题。
C
简单题。
D
简单题。
维护三指针的时候居然忘记最后加 \(1\) 罚了一发,真是该罚!
E
我们发现边权 \(2^i\) 很特殊,然后联想到一个经典结论:
\[2^1+2^2+2^3+ \cdots + 2^n < 2^ {n+1}
\]
这个式子的证明比较简单,不再赘述。
那么转化到本题,就意味着删第 \(i\) 条边的代价大于删掉前 \(i-1\) 条边的代价总和。
于是考虑贪心,从第 \(m\) 到 \(1\) 条边考虑加边,能加就加,不能加才算进答案,结合先前的结论可以发现这样做是正确的。
实现的时候把初始情况看作 \(n\) 个连通块,加边时使用并查集维护连通性,如果在一次加边后连通块个数变成 \(1\) 了,那么这条边就要删掉。
#include <bits/stdc++.h>
#define int long long
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define bck(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
namespace Mycode {...}
using namespace Mycode;
const int N = 2e5 + 5;
const int mod = 998244353;
int n, m, fa[N], U[N], V[N], w[N], ans;
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
signed main() {
n = rd(), m = rd(); w[0] = 1;
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= m; i++) {
U[i] = rd();
V[i] = rd();
w[i] = w[i - 1] * 2 % mod;
}
int bel = n;
for(int i = m; i >= 1; i--) {
int fx = find(U[i]), fy = find(V[i]);
if(fx != fy) {
if(bel > 2) {
fa[fx] = fy;
--bel;
}
else ans = (ans + w[i]) % mod;
}
}
cout << ans << '\n';
return 0;
}
F
一眼就是个与求树的直径类似的树形 DP。
考虑蜈蚣图的构造(以下所有度数都是在蜈蚣图中的度数,中间节点即为题目描述的图中位居中间行的 \(4\) 个节点,即蜈蚣的身体):
- 对于长度为 \(1\) 的,中间节点度数为 \(2\)。
- 对于长度大于 \(1\) 的,最前面和最后面中间节点的度数为 \(3\),其他中间节点度数为 \(4\)。
然后我们就想到对点的度数分类进行转移。
记 \(u\) 节点为当前考虑的节点,\(f_u\) 为以 \(u\) 为起点(最前面中间节点),蜈蚣延伸到 \(u\) 的子树内的最大长度,\(d_u\) 为 \(u\) 节点的度数,\(ans\) 为最终答案,将最前面和最后面中间节点称作蜈蚣图的端点,则有如下转移:
- \(d_u\le 2\),\(u\) 不能当中间节点,\(f_u=0\)。
- \(d_u=3\),\(u\) 只能当端点,向上贡献给父亲的 \(f_u=1\),同时记 \(u\) 的所有儿子中的最大 \(f\) 值为 \(g\),则 \(ans\) 与 \(g+1\) 比大小更新。
- \(d_u>3\),\(u\) 可以当所有中间节点,那么记所有儿子中的最大两个 \(f\) 值为 \(g_1,g_2\),钦定 \(g_1 \le g_2\)。那么若没有向父亲贡献,则有 \(ans\) 与 \(g_2+1\) 比大小或与 \(g_1+g_2+1\) 比大小更新(分别对应当端点和非端点中间节点,当然直接与 \(g_1+g_2+1\) 比大小即可)。若需要向父亲贡献,则只能是非端点中间节点,所以 \(f_u=g_2+1\)。
思路梳理好之后代码实现就很容易了。注意 \(N\ge 3\),所以 \(ans\) 的初值是 \(1\)。
赛时想到了按度数分类,但是分 \(d_u>3\) 的时候崩了,饮恨离场。
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define bck(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
namespace Mycode {...}
using namespace Mycode;
const int N = 2e5 + 5;
int tim, n, cnte, f[N], ans, d[N], hd[N];
struct edges {
int nxt, to;
} ed[N * 2];
void adde(int u, int v) {
ed[++cnte] = (edges) {hd[u], v};
hd[u] = cnte;
return;
}
void slv(int u, int fa) {
if(d[u] <= 2) {
f[u] = 0;
for(int i = hd[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if(v == fa) continue;
slv(v, u);
}
}
else if(d[u] == 3) {
f[u] = 1;
int g = 0;
for(int i = hd[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if(v == fa) continue;
slv(v, u);
cmax(g, f[v]);
}
cmax(ans, g + 1);
}
else {
int g1 = 0, g2 = 0;
for(int i = hd[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if(v == fa) continue;
slv(v, u);
if(g2 < f[v]) g1 = g2, g2 = f[v];
else if(g1 < f[v]) g1 = f[v];
}
f[u] = g2 + 1;
cmax(ans, g1 + g2 + 1);
}
return;
}
void solve() {
memset(hd, 0, sizeof(hd));
memset(f, 0, sizeof(f));
memset(d, 0, sizeof(d));
cnte = 0, ans = 1;
n = rd();
for(int i = 1, u, v; i < n; i++) {
u = rd(), v = rd();
adde(u, v), adde(v, u);
d[u]++, d[v]++;
}
slv(1, 1);
write(ans);
putchar('\n');
return;
}
int main() {
tim = rd();
while(tim--) solve();
return 0;
}

浙公网安备 33010602011771号