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] 建造军营 - 董晓 - 博客园
浙公网安备 33010602011771号