题解:CF1239F Swiper, no swiping!

posted on 2024-08-01 11:04:17 | under | source

膜拜 yyc

大体思路:不断考虑特殊情况的解法,最终拼出正解。

称度数对 \(3\) 取模后为 \(k\) 的点为 \(k\) 度点,度数为 \(d_i\)

  • 若存在 \(0\) 度点:直接保留它即可。

  • 否则,若存在至少两个 \(1\) 度点:考虑两个 \(1\) 度点 \(x,y\),那么 \(x,y\) 之间的最短路(边权为 \(1\))必然是 \(2\) 度点构成的链,那么将 \(x,y\) 链保留即可。

  • 否则,若存在 \(2\) 度点构成的简单环:保留简单环即可,怎么找?建 dfs 树,对每个点找离它最近的一条返祖边。

  • 否则,若没有 \(1\) 度点:容易证明不可能,因为 \(\forall i, 2\mid d_i\),肯定有欧拉回路,即环,而环由若干简单环组成。

  • 否则:为若干棵树的叶子向唯一一个 \(1\) 度点连边的形式,易证,树的个数 \(\ge 2\)——考虑菊花图,那么此时叶子个数 \(\bmod\ 3=2\),现在让一个叶子 \(p\) 生长,\(p\) 的儿子个数 \(\bmod\ 3=1\),总共叶子个数取模后 \(+1\)\(-1\),故一棵树的儿子个数 \(\bmod\ 3=2\),故不可能只有一棵树。

  • 接着分析,发现可以以 \(1\) 度点为中心构造一个“8 字形图”,具体而言,对 \(2\) 个不同的树各取 \(2\) 个叶子,然后 “8 字形”的顶点就是这两组点各自的 lca。

  • 注意事项:此题“叶子”严格定义应该是与 \(1\) 度点相连的点,选取的 “8 字形”的两个叶子之间的路径不能有其他叶子(例如样例),实现的话可以任意选取一个叶子跑 bfs 直到找到另一个叶子。

  • 此外假如构造方案为全集或空集,则返回无解。

代码

十分丑陋。

#include<bits/stdc++.h>
using namespace std;

const int N = 5e5 + 5;
int T, n, m, u, v, du[N];
int vis[N], from[N];
int cir, dep[N];
int Vis[N], From[N], Ans[N], Leaf[N];
vector<int> to[N], ans, ct[5], lby[N];

inline void print(){
	if(ans.size() == 0 || ans.size() == n){
		puts("No");
		return ;
	}
	printf("Yes\n%d\n", n - ans.size());
	for(auto i : ans) Ans[i] = 1;
	for(int i = 1; i <= n; ++i) if(!Ans[i]) printf("%d ", i);
	printf("\n");
}
inline void dfs(int u, int fa, int col){
	from[u] = fa, dep[u] = dep[fa] + 1, vis[u] = col;
	
	int yyc = 0;
	for(auto v : to[u])
		if(v ^ fa && du[v] % 3 == 2){
			if(vis[v] && (!yyc || dep[v] > dep[yyc])) yyc = v;
		}	
	if(yyc && !cir){
		cir = 1;
		int tp = u;
		while(tp ^ yyc) ans.push_back(tp), tp = from[tp];
		ans.push_back(yyc);
	}
	for(auto v : to[u])
		if(v ^ fa && du[v] % 3 == 2 && !vis[v]) dfs(v, u, col);
}
inline void Path(int x){
	int y = 0;
	queue<int> q;
	q.push(x), Vis[x] = 1;
	while(!q.empty()){
		int u = q.front(); q.pop();
		for(auto v : to[u])
			if(!Vis[v]){
				Vis[v] = 1, From[v] = u;
				if(!Leaf[v]) q.push(v);
				else {y = v; break;}
			}
		if(y) break;
	}
	int tp = y;
	while(tp) ans.push_back(tp), tp = From[tp];
}
inline void clr(){
	ans.clear(), ct[0].clear(), ct[1].clear(), ct[2].clear(), cir = 0;
	for(int i = 1; i <= n; ++i)
		du[i] = vis[i] = from[i] = dep[i] = Vis[i] = From[i] = Ans[i] = Leaf[i] = 0, to[i].clear(), lby[i].clear();
}
signed main(){
	cin >> T;
	while(T--){
		scanf("%d%d", &n, &m), clr();
		for(int i = 1; i <= m; ++i){
			scanf("%d%d", &u, &v);
			to[u].push_back(v), to[v].push_back(u);
			++du[u], ++du[v];
		}
		for(int i = 1; i <= n; ++i) ct[du[i] % 3].push_back(i);
		if(!ct[0].empty()) ans.push_back(ct[0][0]);
		else if(ct[1].size() >= 2){
			int x = ct[1][0], y = 0;
			queue<int> q;
			q.push(x), vis[x] = 1; 
			while(!q.empty()){
				int u = q.front(); q.pop();
				for(auto v : to[u])
					if(!vis[v]){
						vis[v] = 1, from[v] = u;
						if(du[v] % 3 == 2) q.push(v);
						else if(du[v] % 3 == 1){
							y = v;
							break;
						}
					}
				if(y) break;
			}
			int tp = y;
			while(tp) ans.push_back(tp), tp = from[tp];
		}
		else{
			for(auto i : ct[2]) if(!vis[i])	dfs(i, 0, i);
			if(!cir){
				int mid = ct[1][0]; 
				ans.push_back(mid), Vis[mid] = 1; 
				for(auto i : to[mid]) Leaf[i] = 1, lby[vis[i]].push_back(i);
				int cnt = 0;
				for(int i = 1; i <= n; ++i)
					if(!lby[i].empty()){
						++cnt;
						if(cnt <= 2) Path(lby[i][0]);
					}
			}
		}
		print();
	}
	return 0;
}
posted @ 2026-01-15 08:16  Zwi  阅读(1)  评论(0)    收藏  举报