P11093 [ROI 2021 Day 2] 树制游戏 题解
考虑对于一个解,将每对 \((e_1,e_2)\) 中 \(e_1\) 的终点权值 \(+1\),\(e_2\) 的起点权值 \(-1\),那么最终每个点的权值一定是 \(0\)。
考虑先将每条边的终点权值都 \(+1\),那么现在要做的就是选一些点将其起点和终点的权值都 \(-1\),使得最终每个点的权值为 \(0\),于是边的方向就不重要了。
因为是一颗树,考虑随便取一个点位根,从叶子节点开始贪心选。每次如果当前考虑的点权值 \(>0\),就从它连到父亲的边中随便选出若干个使其权值减到 \(0\),如果 \(<0\) 则无解。记录下选出的边,最终和没选择的边匹配即可。
参考代码:
#include<bits/stdc++.h>
#define mxn 300003
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
struct edge{
int x,y;
}e[mxn];
int n,m,c[mxn];
int tot,hd[mxn],vr[mxn<<1],ed[mxn<<1],nx[mxn<<1];
bool v[mxn],b[mxn];
vector<int>s1[mxn],s2[mxn];
inline void add(int x,int y,int z){
vr[++tot]=y,ed[tot]=z,nx[tot]=hd[x],hd[x]=tot;
}
void dfs(int x,int fa){
v[x]=1;
for(int i=hd[x],y;i;i=nx[i])if(!v[y=vr[i]]){
dfs(y,x);
}
if(c[x]<0||c[x]>2){
puts("No");
exit(0);
}
if(!c[x])return;
if(!fa){
puts("No");
exit(0);
}
vector<int>s;
for(int i=hd[x],y;i;i=nx[i])if(vr[i]==fa){
s.pb(ed[i]);
}
if(c[x]>s.size()){
puts("No");
exit(0);
}
rept(i,0,c[x]){
c[fa]--,b[s[i]]=1;
}
}
inline void out(int x,int y){
cout<<e[x].x<<" "<<e[x].y<<" "<<e[y].x<<" "<<e[y].y<<'\n';
}
signed main(){
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<=m;++i){
scanf("%d%d",&x,&y);
e[i]={x,y},c[x]++;
add(x,y,i),add(y,x,i);
}
rep(i,1,n)if(!v[i])dfs(i,0);
rep(i,1,m){
if(b[i])s2[e[i].y].pb(i);
else s1[e[i].x].pb(i);
}
puts("Yes");
rep(i,1,n){
rept(j,0,s1[i].size())out(s2[i][j],s1[i][j]);
}
return 0;
}