电压

vj 传送门

简化题意

给定若干无向图,点值 \(\in \left\{0,1\right\}\), 对于每条边,若边的两端点权不同,边被标记为可行,否则为不可行,对于每条边,判断是否存在一种给点赋值的方法,使得图中只有该边不可行,而其他边均可行,最后输出这种边的数量。

做法

先明确思路,这里的可行问题显然是与奇偶性有关的。

  • 性质一:
    如果我们现在有个奇环,无论怎么标都必然有条边不可行。
  • 性质二:
    反之为偶环的话,必然可以找到一种所有边均可行的方案。同时,如果该环中存在不可行的边,这种边的边数必定为偶数。
  • 性质三:
    同时又因为我们只能有一条边不可行,所以可以发现,这条边必须出现在所有奇环中,并且不出现在任何偶环中 (否则由性质二可以得到必然存在两条及以上不可行的边)。

有了这三个性质这个问题就简单了,我们的任务就是求出有多少边出现在所有奇环中,并且不出现在任何偶环中,通过树上差分统计一下即可。

关于树上差分在稍微补充一点,通过 dfs 序操作后,我们可以把图上的边分为树边和返祖边,而形成环的方式,就是若干条树边加上一条返祖边,所以这里我们对每条返祖边进行操作,对当前 \(u\) 加 1, 对返祖边通向的 \(v\) 减 1 即可。

注意点就是由于我们是边的差分,所以最后判断一下这个点是不是根节点,另外如果奇环只有一个的话,我们是可以修改非树边的,注意最后 +1。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,dep[N],vis[N],odd[N],even[N],ans,ff[N];
int tot,head[N];
struct node{
	int to,nxt;
}edge[N<<1];
void addedge(int u,int v){
	edge[tot]={v,head[u]};
	head[u]=tot++;
}
void dfs(int u,int fa,int idx){
	ff[u]=fa,dep[u]=dep[fa]+1,vis[u]=1;
	for(int i=head[u];~i;i=edge[i].nxt){
		int v=edge[i].to;
		if((i^1)==idx)continue;
		if(!vis[v]){
			dfs(v,u,i);
			odd[u]+=odd[v];
			even[u]+=even[v];
		}else{
			if(dep[v]>dep[u])continue;
			if((dep[u]-dep[v])&1)
				even[u]++,even[v]--,even[0]++;
			else
				odd[u]++,odd[v]--,odd[0]++;
		}
	}
}
int main(){
	memset(head,-1,sizeof head);
	cin>>n>>m;
	for(int i=1,u,v;i<=m;++i){
		cin>>u>>v;
		addedge(u,v);
		addedge(v,u);
	}
	for(int i=1;i<=n;++i){
		if(!vis[i])dfs(i,0,-1);
	}
	for(int i=1;i<=n;++i){
		if(!even[i]&&odd[i]==odd[0])ans++;
	}
	cout<<odd[0]<<endl;
	if(odd[0]==1)ans++;
	cout<<ans;
}
posted @ 2025-07-19 10:23  黑昼白夜  阅读(11)  评论(0)    收藏  举报