題意:給一個無向圖,問每次加一條邊剩余的橋是多少。
這題有更優的方法,不用縮點,不過為了學習縮點還是學習了下面的算法。
&&吐槽,這題數據弱,時限寬,各種姿勢的暴力都隨便過。
先求出邊雙連通分支,然后縮點成一顆新的樹。
怎么縮呢:只把不屬同一雙連通分支的邊加到新樹中。
顯然的是,每在兩個點之間添加一條邊,那么這兩個點到它們的最近公共祖先之間的邊都不再是橋。這里允許每次暴力求LCA,然后把兩個點到lca之間有多少邊算出來,就是減少的橋。但有個問題這樣的話有些邊會被減多次,這里用並查集來解決,每次把新組成的一個雙連通分支放到一個並查集里,如果某點在並查集里,就直接跳到根上,雖然損失了一些時間,但是避免了重復計算。
【代碼】
/* *********************************************** Author :angon ************************************************ */ #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <stack> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; #define showtime fprintf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC) #define lld %I64d #define REP(i,k,n) for(int i=k;i<n;i++) #define REPP(i,k,n) for(int i=k;i<=n;i++) #define scan(d) scanf("%d",&d) #define scanl(d) scanf("%I64d",&d) #define scann(n,m) scanf("%d%d",&n,&m) #define scannl(n,m) scanf("%I64d%I64d",&n,&m) #define mst(a,k) memset(a,k,sizeof(a)) #define LL long long #define N 110005 #define mod 1000000007 inline int read(){int s=0;char ch=getchar();for(; ch<'0'||ch>'9'; ch=getchar());for(; ch>='0'&&ch<='9'; ch=getchar())s=s*10+ch-'0';return s;} struct Edge { int v,next; }edge[N*4],edge2[N*4]; int head[N],tot; int head2[N],tot2; int belong[N],Stack[N],inStack[N]; int low[N],dfn[N]; int scc,TimeN,top; int deep[N],pre[N],p[N]; void addedge(int u,int v) { edge[tot].v=v; edge[tot].next=head[u]; head[u] = tot++; } void addedge2(int u,int v) { edge2[tot2].v=v; edge2[tot2].next=head2[u]; head2[u] = tot2++; } void tarjan(int u,int fa) { dfn[u] = low[u] = ++TimeN; Stack[top++] = u; inStack[u] = 1; for(int i=head[u]; ~i; i=edge[i].next) { int v=edge[i].v; if(v==fa) continue; if(!dfn[v]) { tarjan(v,u); low[u] = min(low[u],low[v]); } else if(inStack[v]) low[u] = min(low[u],dfn[v]); } if(dfn[u]==low[u]) { int v; scc++; do { v = Stack[--top]; inStack[v] = 0; belong[v] = scc; }while(v!=u); } } void init() { mst(head,-1); tot=0; mst(head2,-1); tot2=0; mst(inStack,0); mst(dfn,0); scc = top = TimeN = 0; } int find(int x) { if(p[x]==x) return x; return p[x]=find(p[x]); } void dfs(int u,int fa,int d) { pre[u]=fa; deep[u]=d; for(int i=head2[u]; ~i; i=edge2[i].next) { int v=edge2[i].v; if(v==fa) continue; dfs(v,u,d+1); } } int LCA(int u,int v) { while(u!=v) { if(deep[u]>deep[v]) u = pre[u]; else if(deep[u]<deep[v]) v = pre[v]; else u=pre[u],v=pre[v]; u=find(u); v=find(v); } return u; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n,m,q,u,v; int cas=1; while(~scann(n,m) && (n||m)) { init(); while(m--) { scann(u,v); addedge(u,v); addedge(v,u); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,-1); for(u=1;u<=n;u++) for(int i=head[u]; ~i; i=edge[i].next) { v=edge[i].v; if(belong[u]==belong[v]) continue; addedge2(belong[u],belong[v]); } int cnt=scc-1; dfs(1,-1,0); REPP(i,1,n) p[i]=i; scan(q); printf("Case %d:\n", cas++); while(q--) { scann(u,v); u=belong[u]; v=belong[v]; u = find(u); v = find(v); int lca = LCA(u,v); //找到最近公共祖先 int x=0; while(u!=lca) //路徑上有多少條邊 { x++; p[u]=lca; u=pre[u]; u=find(u); } while(v!=lca) { x++; p[v]=lca; v=pre[v]; v=find(v); } cnt-=x; printf("%d\n",cnt); } printf("\n"); } return 0; }
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。