POJ3177 Redundant Paths
用时:断断续续地debug,大概用了120min?
板子题.
求将一个无向图变成边双联通图最少的加边数.
结论:设原图叶子节点(入度为1的)的数量为\(x\),答案即为\((x+1)/2\)
证明:将叶子节点两两相连.
首先\(tarjan\)求出所有强连通分量,只保留割边.
缩点后判断有多少入度为1的节点即可.
bug:
因为要用^1
来取一条边的反向边,所以下标必须从偶数开始.
但是写链前时,i==0是退出条件,所以要从2开始!我是傻逼(1/1)
code
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;
const int maxn = 1e5+10;
int n,m,x,y,sum,cnt,now,num,top,inn[maxn];
int to[maxn],head[maxn],nxt[maxn];
int dfn[maxn],low[maxn],sta[maxn],col[maxn];
bool insta[maxn],cut[maxn];
void add(int x,int y){
to[++cnt] = y;
nxt[cnt] = head[x];
head[x] = cnt;
}
void tarjan(int u,int fa){
dfn[u] = low[u] = ++now;
sta[++top] = u;
insta[u] = true;
for(int i = head[u];i;i = nxt[i]){
int v = to[i];
if(v == fa) continue;
if(!dfn[v]){
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] > dfn[u])
cut[i] = cut[i^1] = true;
}
else if(insta[v])
low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u]){
int v;
num++;
do{
v = sta[top--];
insta[v] = false;
col[v] = num;
} while(v != u);
}
}
int main(){
scanf("%d%d",&n,&m);
cnt = 1;
for(int i = 1;i <= m;i++){
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i = 1;i <= n;i++)
if(!dfn[i]) tarjan(i,i);
for(int i = 2;i <= m*2+1;i++)
if(cut[i])
inn[col[to[i]]]++;
for(int i = 1;i <= num;i++)
if(inn[i] == 1)
sum++;
printf("%d\n",(sum+1)/2);
return 0;
}