2025CSP-S模拟赛21 比赛总结
2025CSP-S模拟赛21
| T1 | T2 | T3 | T4 |
|---|---|---|---|
| 55 TLE | 16 WA | 0 WA | 8 WA |
排名:8/19;总分:79
T1 是 \(O(n\log^2 n)\) 做法,几近正解。余下的皆为骗分。
T1 kotori
这个赛时写的感觉几近正解。此处梳理我的思路。
首先考虑到所有启动的投票装置(下文称为特殊点)之间的简单路径后构成一个连通块,这个是显然的。那么,查询一个点的时候,由于他能走到一个特殊点,那么他就一定可以走到当前这个连通块内的所有点,以及他与其中一个特殊点之间的简单路径上的点。考虑树剖维护树中简单路径上的点的最小值,然后考虑在全局维护一个最小值用来维护连通块内点的最小值。时间复杂度 \(O(n \log^2n)\)。
然后就可以引申出正解。我们把第一个成为特殊点的点看作根,那么我们就只需维护根链上的最小值。而这个值有时不变的,所以一遍 dfs 处理即可。时间复杂度 \(O(n)\)。
#include <bits/stdc++.h>
#define il inline
using namespace std;
const int bufsz = 1 << 20;
char ibuf[bufsz], *p1 = ibuf, *p2 = ibuf;
#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, bufsz, stdin), p1 == p2) ? EOF : *p1++)
il int read() {
int x = 0; char ch = getchar(); bool t = 0;
while (ch < '0' || ch > '9') {t ^= ch == '-'; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return t ? -x : x;
}
#undef getchar()
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10;
int n, qq;
vector<int> G[N];
int f[N];
void dfs(int x, int val, int fa) {
f[x] = min(val, x);
for (int y : G[x]) {
if (y == fa) continue;
dfs(y, f[x], x);
}
}
int main() {
n = read(), qq = read();
for (int i = 1; i < n; i++) {
int x = read(), y = read();
G[x].push_back(y);
G[y].push_back(x);
}
int lastans = 0;
int lstx = 0;
int mn = INF;
while (qq--) {
int op = read(), x = read();
x = (x + lastans) % n + 1;
if (op == 1) {
if (!lstx) {
lstx = x;
dfs(x, INF, 0);
}
mn = min(mn, f[x]);
} else {
lastans = min(mn, f[x]);
printf("%d\n", lastans);
}
}
return 0;
}
T2 charlotte
这个换根挺屎的。神秘调了八九个小时。
设 \(siz_u\) 表示 \(u\) 子树中有棋子的点的个数,\(g_u\) 表示子树内所有棋子到 \(u\) 的距离和,\(f_u\) 表示一系列操作后能使 \(g_u\) 达到的最小值。直接说转移了:
\[\begin{array}{rl}
siz_u & = & tag_u+\sum_{v \in son_u} siz_v\\
g_u & = & \sum_{v\in son_u} g_v+siz_v\\
f_u & = &\max_{v\in son_u}\{f_v+siz_v-(g_u-g_v-siz_v)\}
\end{array}
\]
然后就是如果 \(f_u\) 小于 0,应赋值为 \(g_u \bmod 2\)。具体看这儿吧,也懒得写了。答案就是 \(\min\{g_u/2\} ,(f_u=0)\)。
然后上个换根就行了。
这个换根还是很吃操作的。由于咱这个 \(f\) 的转移是取最大值,所以还得记录次大值。然后就是犯了个傻逼错误,在判全小于 0 的时候最开始是在转移的过程中判,就导致在换根时直接爆炸。(鸣谢 zhangxy__hp)实则只需判断最大值是否小于 0 即可。
这题貌似换根这个次大值还可以利用 set 来做。但是,你知道吧,STL 这东西我认为还是慎用。
#include <bits/stdc++.h>
#define il inline
#define int long long
using namespace std;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
int n, tag[N];
char s[N];
vector<int> G[N];
int f[N], g[N], siz[N];
int f1[N], nxt[N], g1[N];
void dfs(int x, int fa) {
siz[x] = tag[x];
for (int y : G[x]) {
if (y == fa) continue;
dfs(y, x);
siz[x] += siz[y];
g[x] += g[y] + siz[y];
}
int flag = 1;
for (int y : G[x]) {
if (y == fa) continue;
int num = f[y] + siz[y] - (g[x] - g[y] - siz[y]);
flag &= (num < 0);
f[x] = max(f[x], num);
}
if (flag) {
f[x] = g[x] % 2;
}
}
int ss[N];
void dfs1(int x, int fa) {
for (int y : G[x]) {
if (y != fa) {
g1[x] += g[y] + siz[y];
} else {
int sizy = siz[1] - siz[x];
int gy = g1[y] - g[x] - siz[x];
g1[x] += gy + sizy;
}
}
ss[x] = G[x].size();
for (int y : G[x]) {
int num;
if (y != fa) {
num = f[y] + siz[y] - (g1[x] - g[y] - siz[y]);
} else {
int tmp = f[x] + siz[x] - (g1[y] - g[x] - siz[x]);
int gy = g1[y] - g[x] - siz[x];
int sizy = siz[1] - siz[x];
int fy = (tmp != f1[y] ? f1[y] : nxt[y]);
if (tmp != f1[y]) {
fy = f1[y] + g1[y] - gy;
if (fy < 0) fy = gy % 2;
} else {
if (ss[y] == 1) {
fy = 0;
} else {
fy = nxt[y] + g1[y] - gy;
if (fy < 0) fy = gy % 2;
}
}
num = fy + sizy - (g1[x] - gy - sizy);
}
if (num > f1[x]) {
nxt[x] = f1[x];
f1[x] = num;
} else if (num > nxt[x]) {
nxt[x] = num;
}
}
for (int y : G[x]) {
if (y != fa) dfs1(y, x);
}
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> s;
for (int i = 1; i <= n; i++) {
tag[i] = (s[i - 1] == '1');
}
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
for (int i = 1; i <= n; i++) f1[i] = nxt[i] = -INF;
dfs(1, 0);
dfs1(1, 0);
for (int i = 1; i <= n; i++) {
if (f1[i] < 0) f1[i] = g1[i] % 2;
}
int ans = INF;
for (int i = 1; i <= n; i++) {
if (f1[i] == 0) ans = min(ans, g1[i] / 2);
}
cout << (ans < INF ? ans : -1) << '\n';
return 0;
}

浙公网安备 33010602011771号