标签:
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 13190 | Accepted: 5618 |
Description
Input
Output
Sample Input
7 7 1 2 2 3 3 4 2 5 4 5 5 6 5 7
Sample Output
2
Hint
1 2 3Building new paths from 1 to 6 and from 4 to 7 satisfies the conditions.
+---+---+
| |
| |
6 +---+---+ 4
/ 5
/
/
7 +
1 2 3Check some of the routes:
+---+---+
: | |
: | |
6 +---+---+ 4
/ 5 :
/ :
/ :
7 + - - - -
Source
题目大意:
有F个牧场,1<=F<=5000,现在一个牧群经常需要从一个牧场迁移到另一个牧场。奶牛们已经厌烦老是走同一条路,所以有必要再新修几条路,这样它们从一个牧场迁移到另一个牧场时总是可以选择至少两条独立的路。现在F个牧场的任何两个牧场之间已经至少有一条路了,奶牛们需要至少有两条。
给定现有的R条直接连接两个牧场的路,F-1<=R<=10000,计算至少需要新修多少条直接连接两个牧场的路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指没有公共边的路,但可以经过同一个中间顶点
题解:
tarjan求割边(桥)的模板
一个有桥的连通图,如何把它通过加边变成边双连通图?
方法为首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。
其实,本题数据保证是一个连通图,那么桥=缩点数-1;
统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。
AC代码:
#include<cstdio> #include<stack> using namespace std; #define N 5005 struct node{ int v,next; }e[N<<1]; int n,m,tot,head[N],low[N],dfn[N],id[N],in[N],pd,sd; bool mark[N],mp[N][N]; stack<int>s; void add(int x,int y){//判断是不是父边 e[++tot].v=y; e[tot].next=head[x]; head[x]=tot; } bool judge(int x,int y){ if((x&1)&&y==x+1) return 1; if(!(x&1)&&y==x-1) return 1; return 0; } void tarjan(int v,int fa){ low[v]=dfn[v]=++pd; s.push(v); mark[v]=1; for(int i=head[v];i;i=e[i].next){ if(judge(i,fa)) continue; int w=e[i].v; if(!dfn[w]){ tarjan(w,i); low[v]=min(low[v],low[w]); } else if(mark[w]){ low[v]=min(low[v],dfn[w]); } } int u; if(low[v]==dfn[v]){ sd++; do{ u=s.top(); s.pop(); id[u]=sd; mark[u]=0; }while(u!=v); } } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); if(!mp[x][y]){ add(x,y);add(y,x); mp[x][y]=mp[y][x]=1; } } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,-1); for(int i=1;i<=n;i++){ for(int j=head[i];j;j=e[j].next){ if(id[i]!=id[e[j].v]){ in[id[i]]++; } } } int ans=0; for(int i=1;i<=sd;i++) if(in[i]==1) ans++; printf("%d\n",(ans+1)/2); return 0; }
标签:
原文地址:http://www.cnblogs.com/shenben/p/5839786.html