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;
}
posted @ 2024-09-30 15:12  zifanwang  阅读(5)  评论(0编辑  收藏  举报