【倍增LCA求次小生成樹】Gym - 101889I - Imperial roads
阿新 • • 發佈:2018-11-10
題目連結<http://codeforces.com/gym/101889/attachments>
題意:
給出若干條無向邊,有q次詢問,每次詢問確定一條邊必須新增的最小生成樹。
題解:
與次小生成樹的思路一樣,先求一遍最小生成樹,然後新增邊,就構成一個環,把環內最大邊刪去即可。
利用倍增LCA的思想,處理簡單路上的最大邊。
#include<bits/stdc++.h> using namespace std; #define pii pair<int,int> typedef long long ll; const int N=1e5+7; int fa[N][20],maxn[N][20],d[N],dis[N]; int p[N],edn; int n,m,q; map<pair<int,int>,int>mp; struct Edge{ int u,v,w,nxt; Edge(int u=0,int v=0,int w=0,int nxt=0):u(u),v(v),w(w),nxt(nxt){} bool operator<(const Edge a)const{ return w<a.w; } }edge[N*4],e[N*4]; void add(int u,int v,int w){ edge[++edn]=Edge(u,v,w,p[u]);p[u]=edn; edge[++edn]=Edge(v,u,w,p[v]);p[v]=edn; } void dfs(int u,int f){ for(int i=p[u];~i;i=edge[i].nxt){ int v=edge[i].v; if(v==f) continue; d[v]=d[u]+1; fa[v][0]=u; maxn[v][0]=edge[i].w; dfs(v,u); } } void init(){ for(int j=1;(1<<j)<=n;j++){ for(int i=1;i<=n;i++){ fa[i][j]=fa[fa[i][j-1]][j-1]; maxn[i][j]=max(maxn[i][j-1],maxn[fa[i][j-1]][j-1]); } } } int lca(int a,int b){ if(fa[a][0]==b||fa[b][0]==a) return 0; int ans=mp[pii(a,b)]; if(d[a]>d[b]) swap(a,b); int f=d[b]-d[a]; int res=0; for(int i=0;(1<<i)<=f;i++){ if((1<<i)&f) res=max(res,maxn[b][i]),b=fa[b][i]; } if(a!=b){ for(int i=(int)log2(N);i>=0;i--){ if(fa[a][i]!=fa[b][i]){ res=max(res,maxn[a][i]); res=max(res,maxn[b][i]); a=fa[a][i]; b=fa[b][i]; } } res=max(res,maxn[a][0]); res=max(res,maxn[b][0]); } return ans-res; } int f[N]; int fd(int x){return x==f[x]?x:f[x]=fd(f[x]);} int main() { memset(p,-1,sizeof(p));edn=-1; memset(fa,-1,sizeof(fa)); scanf("%d%d",&n,&m); int u,v,w; for(int i=1;i<=m;i++){ scanf("%d%d%d",&u,&v,&w); mp[make_pair(u,v)]=w; mp[make_pair(v,u)]=w; e[i]=Edge(u,v,w,0); } sort(e+1,e+1+m); int ans=0; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=m;i++){ u=e[i].u,v=e[i].v,w=e[i].w; int fu=fd(u),fv=fd(v); if(fu==fv) continue; f[fu]=fv; ans+=w; add(u,v,w); } d[1]=1;dfs(1,1); init(); scanf("%d",&q); while(q--){ scanf("%d%d",&u,&v); printf("%d\n",ans+lca(u,v)); } }