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;
}

 

 

posted @ 2022-07-15 09:39  董晓  阅读(451)  评论(0编辑  收藏  举报