D31 圆方树 P5236【模板】静态仙人掌
视频链接:394 圆方树 P5236【模板】静态仙人掌_哔哩哔哩_bilibili
// Luogu P5236 【模板】静态仙人掌 #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=20000,M=N*3; int n,m,Q; struct edge{int v,w,ne;}e[M]; int h1[N],h2[N],idx=1;//建图 int dfn[N],low[N],tim;//tarjan int s[N],sc[N],fa[N],fw[N],fe[N],cn; int f[N][14],dep[N],d[N];//lca int A,B;//存lca的两个儿子 void add(int h[],int a,int b,int c){ e[++idx]={b,c,h[a]};h[a]=idx; } void build_tree(int u,int v,int w){ int sum=w; for(int k=v;k!=u;k=fa[k]){ s[k]=sum; //u...k的环长 sum+=fw[k]; //前缀和 } // s[u]=sc[u]=sum; add(h2,u,++cn,0); for(int k=v;k!=u;k=fa[k]){ sc[k]=sum; //总环长 add(h2,cn,k,min(s[k],sum-s[k])); } } void tarjan(int u,int ine){ dfn[u]=low[u]=++tim; for(int i=h1[u];i;i=e[i].ne){ int v=e[i].v,w=e[i].w; if(!dfn[v]){ //若没有访问 //fw:存边权,fe:存入边 fa[v]=u,fw[v]=w,fe[v]=i; tarjan(v,i); low[u]=min(low[u],low[v]); if(dfn[u]<low[v]) //非环边 add(h2,u,v,w);//直接加边 } else if(i!=(ine^1)) //构成环 low[u]=min(low[u],dfn[v]); } for(int i=h1[u];i;i=e[i].ne){ int v=e[i].v,w=e[i].w; if(dfn[u]<dfn[v]&&fe[v]!=i) build_tree(u,v,w); //建树 } } void dfs(int u,int father){ dep[u]=dep[father]+1; f[u][0]=father; for(int k=1;k<=13;k++) f[u][k]=f[f[u][k-1]][k-1]; for(int i=h2[u];i;i=e[i].ne){ int v=e[i].v,w=e[i].w; d[v]=d[u]+w; dfs(v,u); } } int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); for(int k=13;k>=0;k--) if(dep[f[u][k]]>=dep[v]) u=f[u][k]; if(u==v) return u; for(int k=13;k>=0;k--) if(f[u][k]!=f[v][k]){ u=f[u][k]; v=f[v][k]; } A=u,B=v;//存lca的两个儿子 return f[u][0]; } int main(){ scanf("%d%d%d",&n,&m,&Q); cn=n; while(m--){ int a,b,c; scanf("%d%d%d",&a,&b,&c); add(h1,a,b,c),add(h1,b,a,c); } tarjan(1,-1);//找环建树 dfs(1,0);//lca打表 while(Q--){ int u,v; scanf("%d%d",&u,&v); int p=lca(u,v);//找lca if(p<=n) //若是圆点 printf("%d\n",d[u]+d[v]-d[p]*2); else{ //若是方点 int len=abs(s[A]-s[B]); int dAB=min(len,sc[A]-len); int dis=dAB+d[u]-d[A]+d[v]-d[B]; printf("%d\n",dis); } } return 0; }