题目
题目描述
电路由个节点和根细长的电阻组成。节点编号为。
每个节点可设定为两种电平之一:高电平或者低电平。每个电阻连接两个节点,只有一端是高电平,另一端是低电平的电阻才会有电流流过。两端都是高电平或者低电平的电阻不会有电流流过。
试求:有多少个电阻,可以通过调节各节点的电压,使得「没有电流流经该电阻,且其他根电阻中都有电流流过」。
输出输出格式
(就是你想的那样)
数据范围与约定
对于所有测试数据,,不保证图是连通的,不保证没有重边。
思路
本质是类似 二分图 的东西。说白了就是找到 所有奇环的公共边。
但是与完全删掉这条边不同。这条边 不能在偶环上。否则就会……(试一试就知道了)
然后要找到一个环。不用等神奇算法,只需要建一个树,然后考虑 一条 非树边与树边构成的环。
为什么不用考虑两条边的?可以 将其拆开 成两个上面那种类型的。发现同效。
为什么不用考虑三条边的?也可以拆!
树上差分就好。没了。不连通啥的,不重要。
还有一个有意思的地方——在无向图里,树没有横向边。你可以直接找到!妙不可言!
哦,对了,可别把每个联通子图中树的根与其父节点的边统计进去(因为它不存在!)。
代码
#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;
}
来源:https://blog.csdn.net/qq_42101694/article/details/102750266