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;
}

T3 sagiri

T4 chtholly

posted @ 2025-07-19 14:22  Zctf1088  阅读(74)  评论(0)    收藏  举报