2025 XCPC 浙江省赛 FLM

F. Challenge NPC III

多源最短路、次短路。

对同种颜色跑BFS,过程中维护每条路的起点,由于同种颜色自己到自己就是最短,所以最后判断次短路是否小于 \(k\) 即可。

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n, m, k;
	cin >> n >> m >> k;

	vector c(50, vector<int>());
	for (int i = 0; i < n; i += 1) {
		int x;
		cin >> x;
		c[--x].push_back(i);
	}

	vector e(n, vector<int>());
	for (int i = 0; i < m; i += 1) {
		int u, v;
		cin >> u >> v;
		u -= 1, v -= 1;
		e[u].push_back(v);
	}

	const int inf = 1 << 30;

	for (int i = 0; i < 50; i += 1) {
		if (c[i].size() <= 1) {
			continue;
		}
		vector<array<int,2>> dis(n, {inf, inf}), vis(n, {-1,-1});

		queue<int> Q;
		for (auto &u : c[i]) {
			dis[u] = {0, inf};
			vis[u] = {u, -1};
			Q.push(u);
		}
		while (Q.size()) {
			auto u = Q.front();
			Q.pop();

			for (auto &v : e[u]) {
				bool f = 0;
				for(int i : {0, 1}){
					for(int j : {0, 1}){
						if(vis[u][i] == vis[v][j] || vis[u][i] < 0) continue;
						if(dis[u][i] + 1 < dis[v][j] && vis[v][j ^ 1] != vis[u][i]){
							dis[v][j] = dis[u][i] + 1;
							vis[v][j] = vis[u][i];
							f = 1;
						}
					}
				}
				if(f){
					Q.push(v);
				}
			}
		}

		for (auto &u : c[i]) {
			if (dis[u][1] < k) {
				cout << "NO\n";
				return;
			}
		}
	}

	cout << "YES\n";

}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}

L. Nailoongs Always Lie

基环树 \(dp\)

\(n\) 个点,\(n\) 条边,那么一定会形成环,所以这是一个基环树森林,将环上一条边断开,就是两次树上 \(dp\),然后对多个联通块的最大值取和即可。

如果 \(u\) 是奶龙,那么 \(v\) 一定不是奶龙;如果 \(u\) 不是奶龙,那么 \(v\) 可能是也可能不是。
\(dp_{u,0/1}\) 表示 \(u\) 是不是奶龙时的最大答案,则有转移方程:

\[dp_{u,1}=\sum_{v \in Sub_u}dp_{v,0} \]

\[dp_{u,0}=\sum_{v \in Sub_u}\max(dp_{v,0},dp_{v,1}) \]

注意断边时的 \(dp\) 值处理。

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int n;
	cin >> n;

	vector e(n + 1, vector<int>());
	auto fe = e;
	for (int i = 1; i <= n; i += 1) {
		int x;
		cin >> x;
		e[i].push_back(x);
		fe[x].push_back(i);
	}

	int mark = 0;
	vector<int> vis(n + 1);
	vector<array<int,2>> dp(n + 1);
	auto calc = [&](auto &&self, int u)->int{
		vis[u] = 1;
		dp[u] = {0, 1};
		for (auto &v : fe[u]) {
			if (v == mark) {
				dp[v][1] = -1E9;
				continue;
			}
			self(self, v);
			dp[u][1] += dp[v][0];
			dp[u][0] += max(dp[v][1], dp[v][0]);
		}
		return max(dp[u][0], dp[u][1]);
	};

	auto dfs = [&](auto &&self, int u)->void{
		vis[u] = 1;
		if (e[u].empty()) {
			return;
		}
		if (vis[e[u][0]]) {
			mark = u;
		}
		else{
			self(self, e[u][0]);
		}
	};

	int ans = 0;
	for (int i = 1; i <= n; i += 1) {
		if (vis[i]) {
			continue;
		}
		dfs(dfs, i);
		if (mark == 0) {
			continue;
		}
		int tmp = calc(calc, mark);
		mark = e[mark][0];
		tmp = max(tmp, calc(calc, mark));
		ans += tmp;
	}

	cout << ans << "\n";

	return 0;
}

M. Master of Both VII

思维。

考虑询问所有 \(3 \leq i \leq n-1\)\((1,i)\),结果为 \(d_i\)。对于 \(d_i = 0\) 直接加入答案。

对于一条边 \((x,y)\),其会对 \(x < i < y\) 的询问有 1 的贡献。

由于这些边构成了一些不会在非端点处相交的区间,按照 \(2 \leq i \leq n\) 的顺序维护一个栈。

  • 如果 \(d_i > d_{i-1}\),那么入栈 \(d_i - d_{i-1}\)\(i-1\)
  • 如果 \(d_i < d_{i-1}\),那么弹出 \(d_{i-1} - d_i\) 个栈顶,构成边 \((st_{top}, i)\)

由于不会同时存在 \(i-1\) 开始的区间与 \(i\) 结束的区间,可以证明这个做法的正确性。

时间复杂度 \(O(n)\)

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

void solve() {

	int n;
	cin >> n;

	vector<array<int,2>> edge;

	auto ask = [](int l, int r)->int{
		cout << "? " << l << " " << r << endl;
		int res;
		cin >> res;
		return res;
	};
	auto answer = [&]()->void{
		cout << "! ";
		for (auto &[x, y] : edge) {
			cout << x << " " << y << " ";
		}
		cout << endl;
	};

	stack<int> st;
	int lst = 0;
	for (int i = 3; i <= n; i += 1) {
		int x = 0;
		if (i < n) {
			x = ask(1, i);
		}
		if (x == 0 && i < n) {
			edge.push_back({1, i});
			while(!st.empty()){
				edge.push_back({st.top(), i});
				st.pop();
			}
			lst = x;
			continue;
		}
		if (x > lst) {
			for (int j = 0; j < x - lst; j += 1) {
				st.push(i - 1);
			}
		}
		else if (x < lst) {
			for (int j = 0; j < lst - x; j += 1) {
				edge.push_back({st.top(), i});
				st.pop();
			}
		}
		lst = x;
	}

	answer();

	cin >> n;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int t;
	cin >> t;
	while (t--) {
		solve();
	}

	return 0;
}
posted @ 2025-09-11 16:30  Ke_scholar  阅读(28)  评论(0)    收藏  举报