题意:给一个图,然后依次加进去边,问每次加过边后还有几个桥,之前加入的会影响后面加入的
思路:先将图的桥全部找出来,然后将桥的点标记上,然后不需要缩点,直接进行裸的LCA,再找最近公共祖先的时候,遇到标记的点将结果减1,然后将标记取消,不知道为什么我写的跑的特别慢,别人写的都很快,有神犇知道求指教
#include <vector> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int maxn=100010; vector<int>G[maxn]; vector<int>GG[maxn]; int L[maxn],vis[maxn],E[maxn],stack1[maxn]; int depth[maxn],par[maxn]; int vis1[maxn]; int n,m,k,kk,cnt; void dfs(int x,int fa){ vis[x]=1;L[x]=k;E[x]=k++; int flag=0; for(unsigned int i=0;i<G[x].size();i++){ int t=G[x][i]; if(t!=fa){ if(!vis[t]){ dfs(t,x); par[t]=x; L[x]=min(L[x],L[t]); if(L[t]>E[x]){ cnt++;vis1[t]=1; } }else L[x]=min(L[x],E[t]); }else{ if(flag) L[x]=min(L[x],E[t]); flag++; } } if(L[x]==E[x]){ while(stack1[kk]!=x&&kk>0){ L[stack1[kk-1]]=L[x]; kk--; vis[stack1[kk]]=0; } } } void tarjan(){ kk=0;k=1;dfs(1,1); } int LCA(int u,int v){ int sum=0; while(E[u]>E[v]){ if(vis1[u]==1){ sum++;vis1[u]=0; } u=par[u]; } while(E[v]>E[u]){ if(vis1[v]==1){ sum++;vis1[v]=0; } v=par[v]; } while(u!=v){ if(vis1[u]==1){ sum++;vis1[u]=0; } if(vis1[v]==1){ sum++;vis1[v]=0; } u=par[u];v=par[v]; } return sum; } int main(){ int a,b,t=1,q; while(scanf("%d%d",&n,&m)!=-1){ if(n==0&&m==0) break; for(int i=0;i<maxn;i++){ G[i].clear();GG[i].clear(); } memset(vis,0,sizeof(vis)); memset(vis1,0,sizeof(vis1)); for(int i=0;i<m;i++){ scanf("%d%d",&a,&b); G[a].push_back(b); G[b].push_back(a); } cnt=0;tarjan(); // for(int i=1;i<=n;i++) cout<<E[i]<<" ";cout<<endl; // for(int i=1;i<=n;i++) cout<<vis1[i]<<" ";cout<<endl; scanf("%d",&q); printf("Case %d:\n",t++); while(q--){ scanf("%d%d",&a,&b); if(cnt==0){ printf("%d\n",cnt); continue; } int ans=LCA(a,b); cnt-=ans; printf("%d\n",cnt); } printf("\n"); } return 0; }
时间: 2024-10-08 17:03:46