求对于给定一个连通图,加多少条边可以变成边双连通图。
一个有桥的连通图要变成边双连通图的话,把双连通子图收缩为一个点,形成一颗树。需要加的边为(leaf+1)/2 (leaf为叶子结点个数)。
对于此题,有重边但重边不加入计算。
重边的话,要么在开始去掉,要么用桥来计算入度。
因为桥不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。对于重边而言,只有一对边被标记为桥,而对于所有重边来言,belong【u】和belong【v】都是不一样的,那么如果用belong【u】!=belong【v】作为添加入度条件的话,毫无疑问会多加的。
这么说的话,缩点重新建图的话,用桥来建更好一点,这样新图就不会有重边。
代码(用桥计算):
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cstdio> #include<cmath> #include<map> #include<queue> #include<vector> #include<cstring> #include<algorithm> #define rep(i,a,b) for(int i=(a);i<(b);i++) #define rev(i,a,b) for(int i=(a);i>=(b);i--) #define clr(a,x) memset(a,x,sizeof a) #define INF 0x3f3f3f3f typedef long long LL; using namespace std; const int maxn=200005; const int maxm=2000005; struct node { int u,v; bool operator<(const node &b)const { if(b.u!=u)return u<b.u; return v<b.v; } }da[maxm/2]; int first[maxn],ecnt,u[maxm],v[maxm],nex[maxm]; int low[maxn],dfn[maxn],stck[maxn],belong[maxn]; int indx,top,block,bridge; bool ins[maxn],ecut[maxm]; int n,m; int du[maxn]; void tarjan(int s,int pre) { low[s]=dfn[s]=++indx; stck[top++]=s; ins[s]=1; for(int e=first[s];~e;e=nex[e]) { if(v[e]==pre)continue; if(!dfn[v[e]]) { tarjan(v[e],s); low[s]=min(low[s],low[v[e]]); if(low[v[e]]>dfn[s]) { bridge++; ecut[e]=ecut[e^1]=1; } } else if(ins[v[e]])low[s]=min(low[s],dfn[v[e]]); } if(low[s]==dfn[s]) { int v; block++; do { v=stck[--top]; ins[v]=false; belong[v]=block; }while(v!=s); } } void solve(int n) { clr(dfn,0); clr(ins,0); indx=block=top=0; for(int i=1;i<=n;i++) if(!dfn[i])tarjan(1,0); } void add_(int a,int b) { u[ecnt]=a; v[ecnt]=b; ecut[ecnt]=0; nex[ecnt]=first[a]; first[a]=ecnt++; } int main() { int a,b; while(~scanf("%d%d",&n,&m)) { ecnt=0; clr(first,-1); for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); add_(a,b); add_(b,a); } solve(n); clr(du,0); for(int i=0;i<ecnt;i++) if(ecut[i]) { du[belong[u[i]]]++; } int sum=0; for(int i=1;i<=block;i++) if(du[i]==1)sum++; printf("%d\n",(sum+1)/2); } return 0; }
代码二:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cstdio> #include<cmath> #include<map> #include<queue> #include<vector> #include<cstring> #include<algorithm> #define rep(i,a,b) for(int i=(a);i<(b);i++) #define rev(i,a,b) for(int i=(a);i>=(b);i--) #define clr(a,x) memset(a,x,sizeof a) #define INF 0x3f3f3f3f typedef long long LL; using namespace std; const int maxn=200005; const int maxm=2000005; struct node { int u,v; bool operator<(const node &b)const { if(b.u!=u)return u<b.u; return v<b.v; } }da[maxm/2]; int first[maxn],ecnt,u[maxm],v[maxm],nex[maxm];bool g[5005][5005]; int low[maxn],dfn[maxn],stck[maxn],belong[maxn]; int indx,top,block,bridge; bool ins[maxn],ecut[maxm]; int n,m; int du[maxn]; void tarjan(int s,int pre) { low[s]=dfn[s]=++indx; stck[top++]=s; ins[s]=1; for(int e=first[s];~e;e=nex[e]) { if(v[e]==pre)continue; if(!dfn[v[e]]) { tarjan(v[e],s); low[s]=min(low[s],low[v[e]]); if(low[v[e]]>dfn[s]) { bridge++; ecut[e]=ecut[e^1]=1; } } else if(ins[v[e]])low[s]=min(low[s],dfn[v[e]]); } if(low[s]==dfn[s]) { int v; block++; do { v=stck[--top]; ins[v]=false; belong[v]=block; }while(v!=s); } } void solve(int n) { clr(dfn,0); clr(ins,0); indx=block=top=0; for(int i=1;i<=n;i++) if(!dfn[i])tarjan(1,0); } void add_(int a,int b) { u[ecnt]=a; v[ecnt]=b; ecut[ecnt]=0; nex[ecnt]=first[a]; first[a]=ecnt++; } int main() { int a,b; while(~scanf("%d%d",&n,&m)) { ecnt=0;clr(g,false); clr(first,-1); for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); if(a!=b&&!g[a][b])g[a][b]=g[b][a]=1,add_(a,b),add_(b,a); } solve(n); clr(du,0); for(int i=0;i<ecnt;i++) if(belong[u[i]]!=belong[v[i]]) { du[belong[u[i]]]++; } int sum=0; for(int i=1;i<=block;i++) if(du[i]==1)sum++; printf("%d\n",(sum+1)/2); } return 0; }