题解:P11093 [ROI 2021 Day 2] 树制游戏

· · 题解

考虑对于一个解,将每对 (e_1,e_2)e_1 的终点权值 +1e_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;
}