猫 / Tura Mačkica 题解

这题很明显是一道图论题,无向图部分是一颗基环树。基环树很显然可以转化成树来写,部分分也是这样提醒我们的。在分析一下题目,需要我们构造一个欧拉回路,对于欧拉回路,可以用出度等于入度即出度减入度等于 0 这个性质,那我们先对于已有的边的起点 +1 ,终点 -1,那我们就需要把这整个图都变为值为零的点,而对于每一条边,都有三种情况,正向,不连,反向。又因为这是一个树,所以考虑从叶子开始,往上处理(类似一个拓扑排序),如果这个点的值大于零,就将这个点 -1,它的父亲 +1,即从父亲往下连一条边;小于零同理。如果等于 0 就不用连。当然欧拉回路也需要每个点都联通,这个直接用一个并查集维护即可,最后特判一下即可。

那基环树的情况直接分类讨论一条无向边是正向边还是反向边还是不经过即可。时间复杂度是 \(O(n+m)\)

具体实现看代码吧,应该码风比较好。

#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=2E4+5;
int n,m,A,B;
bool flag[N];
vector<int>e[N],point;
int ans=1e9,f[N],cnt,g[N],fa[N];
pii l[N];
int find(int x){
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(int x,int y){
	x=find(x),y=find(y);
	fa[x]=y;
}
void dfs(int u,int F){
	flag[u]=1;
	for(int v:e[u]){
		if(v==F)continue;
		dfs(v,u);
	}
	if(g[u]<0)g[u]++,g[F]--,cnt++,merge(u,F),point.push_back(u),point.push_back(F);
	else if(g[u]>0)g[u]--,g[F]++,cnt++,merge(u,F),point.push_back(u),point.push_back(F);
} 
void solve(int add){
	memset(flag,0,sizeof flag);
	point.clear();
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=n;i++)g[i]=f[i];
	cnt=0;
	for(int i=1;i<=n;i++)
		if(!flag[i])dfs(i,0);
	for(int i=1;i<=n;i++)
		if(g[i]!=0)cnt=1e9; 
	for(int i=1;i<=m;i++)
		merge(l[i].first,l[i].second);
	int FA=find(l[1].first);
	for(int i:point)
		if(find(i) != FA){
			cnt=1e9;break;
		}
	ans=min(ans,cnt+add);
}
signed main(){
	scanf("%d%d",&n,&m);
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		if(u==v)continue;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	scanf("%d%d",&A,&B);
	for(int i=1,u,v;i<=m;i++){
		scanf("%d%d",&u,&v);
		f[u]--,f[v]++;
		l[i]={u,v};
	}
	solve(0);//这个不经过 
	f[A]--;f[B]++;
	solve(1);//A->B
	f[A]+=2,f[B]-=2;
	solve(1);//B->A 
	if(ans==1e9)puts("-1");
	else printf("%d",m+ans); 
	return 0;
}
posted @ 2025-06-05 20:06  hnczy  阅读(31)  评论(0)    收藏  举报