题解:CF1239F Swiper, no swiping!
posted on 2024-08-01 11:04:17 | under | source
大体思路:不断考虑特殊情况的解法,最终拼出正解。
称度数对 \(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;
}

浙公网安备 33010602011771号