[usOJ4888]電圧

China☆狼群 提交于 2019-12-02 09:26:16

题目

传送门

题目描述
电路由NN个节点和MM根细长的电阻组成。节点编号为1,2,3,,N1,2,3,\cdots,N
每个节点可设定为两种电平之一:高电平或者低电平。每个电阻连接两个节点,只有一端是高电平,另一端是低电平的电阻才会有电流流过。两端都是高电平或者低电平的电阻不会有电流流过。
试求:有多少个电阻,可以通过调节各节点的电压,使得「没有电流流经该电阻,且其他M1M-1根电阻中都有电流流过」。
输出输出格式
(就是你想的那样)
数据范围与约定
对于所有测试数据,2N105,1M2×1052\le N\le 10^5,1\le M\le 2\times 10^5,不保证图是连通的,不保证没有重边。

思路

本质是类似 二分图 的东西。说白了就是找到 所有奇环的公共边
但是与完全删掉这条边不同。这条边 不能在偶环上。否则就会……(试一试就知道了)
然后要找到一个环。不用tarjan\text{tarjan}等神奇算法,只需要建一个dfs\text{dfs}树,然后考虑 一条 非树边与树边构成的环。
为什么不用考虑两条边的?可以 将其拆开 成两个上面那种类型的。发现同效。
为什么不用考虑三条边的?也可以拆
树上差分就好。没了。不连通啥的,不重要。
还有一个有意思的地方——在无向图里,dfs\text{dfs}树没有横向边。你可以直接找到lca\text{lca}!妙不可言!
哦,对了,可别把每个联通子图中dfs\text{dfs}树的根与其父节点的边统计进去(因为它不存在!)。

代码

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

struct Edge{
	int to, nxt;
	Edge(int T=0,int N=0):to(T),nxt(N){}
};
const int MaxN = 100005;
int n, m, head[MaxN];
Edge edge[MaxN<<2];
void addEdge(int a,int b){
	static int cntEdge = 0;
	if(not cntEdge){ // init
		for(int i=1; i<=n; ++i)
			head[i] = -1;
	}
	edge[cntEdge] = Edge(b,head[a]);
	head[a] = cntEdge ++;
	edge[cntEdge] = Edge(a,head[b]);
	head[b] = cntEdge ++;
}

int depth[MaxN], cover[MaxN], odd;
void tarjan(int x,int pre){
	for(int i=head[x]; ~i; i=edge[i].nxt){
		if((i^pre) == 1)
			continue;
		if(not depth[edge[i].to]){
			depth[edge[i].to] = depth[x]+1;
			tarjan(edge[i].to,i);
		}
		else if(depth[edge[i].to] >= depth[x]){
			int y = edge[i].to;
			if((depth[x]+depth[y])&1) // 偶环
				cover[x] ++, cover[y] --;
			else
				cover[x] --, cover[y] ++, odd ++;
		}
	}
}
bool vis[MaxN];
void dfs(int x){
	vis[x] = true;
	for(int i=head[x]; ~i; i=edge[i].nxt)
		if(not vis[edge[i].to]){
			dfs(edge[i].to);
			cover[x] += cover[edge[i].to];
		}
}

bool root[MaxN];
int main(){
	scanf("%d %d",&n,&m);
	for(int a,b; m; --m){
		scanf("%d %d",&a,&b);
		addEdge(a,b);
	}
	for(int i=1; i<=n; ++i)
		if(not depth[i]){
			root[i] = true;
			depth[i] = 1;
			tarjan(i,-1);
			dfs(i);
			for(int j=head[i]; ~j; j=edge[j].nxt)
				if(edge[j].to == i){ // 自环
					++ odd;
					if(odd > 1)
						return not printf("0\n");
				}
		}
	int ans = 0;
	for(int i=1; i<=n; ++i)
		if(not root[i] and cover[i] == odd)
			++ ans;
	if(odd == 1) ++ ans;
	printf("%d\n",ans);
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!