D162 eDCC 缩点 Tarjan 算法 P2860 [USACO06JAN] Redundant Paths G 冗余路径

D162 eDCC 缩点 Tarjan 算法 P2860 [USACO06JAN] Redundant Paths G 冗余路径_哔哩哔哩_bilibili

 

P2860 [USACO06JAN] Redundant Paths G - 洛谷

给一个无向连通图,问至少加多少条边使得整个图变成 边双连通分量

思路

用 Tarjan 对 eDCC 缩点,用 bri[] 数组标记割边,用 dcc[] 数组记录缩点编号

边双连通分量缩点后,得到一棵树,割边就是树边,缩点就是树节点

观察发现,要想让树边割不断,最小代价就是对 叶节点 两两配对连边,如果叶节点是奇数,就多加一条边

如图,缩点后,编号 1,2,3,5,7 是叶子节点,需要连 3 条边

枚举原始边,如果是割边,统计割边端点的度

枚举树节点,统计叶节点的个数 $sum$,答案就是 $\lfloor\frac{sum+1}{2}\rfloor$

参考:D17【模板】割边 Tarjan 算法 - 董晓 - 博客园   D18【模板】eDCC 缩点 Tarjan 算法 - 董晓 - 博客园

 

// eDCC缩点 Tarjan算法 O(n+m)
#include<bits/stdc++.h>
using namespace std;

const int N=5010,M=20010;
int to[M],ne[M],h[N],idx=1; //2,3异或配对
void add(int a,int b){
  to[++idx]=b,ne[idx]=h[a],h[a]=idx;
}
int n,m,sum;
int dfn[N],low[N],stk[N],top,dcc[N],cnt;
int bri[M],deg[N];

void tarjan(int x,int e){
  dfn[x]=low[x]=++dfn[0]; stk[++top]=x;
  for(int i=h[x];i;i=ne[i]){
    int y=to[i];
    if(!dfn[y]){ //若y未访问
      tarjan(y,i);
      low[x]=min(low[x],low[y]);
      
      if(low[y]>dfn[x]) bri[i]=bri[i^1]=1; //标记割边
    }
    else if(i!=(e^1)) //若y已访问且不是反边
      low[x]=min(low[x],dfn[y]);
  }
  
  if(low[x]==dfn[x]){ //若x是edcc的根
    ++cnt;
    while(stk[top+1]!=x) dcc[stk[top--]]=cnt;
  }
}
int main(){
  cin>>n>>m;
  for(int a,b;m--;)cin>>a>>b,add(a,b),add(b,a);

  tarjan(1,0);
  
  for(int i=2;i<=idx;i++) //枚举原始边
    if(bri[i]) deg[dcc[to[i]]]++; //如果是割边,统计割边端点的度

  for(int i=1;i<=cnt;i++) //枚举缩点(树节点)
    if(deg[i]==1) sum++; //统计叶节点个数
  cout<<(sum+1>>1);
}

 

相关题目:E94 Tarjan边双缩点+树形DP P8867 [NOIP2022] 建造军营 - 董晓 - 博客园

 

posted @ 2026-05-21 14:05  董晓  阅读(50)  评论(0)    收藏  举报