总算知道圆方树是个什么玩意儿了……
首先我们构造出这个仙人掌的圆方树(不知道圆方树是什么玩意儿的可以看看yyb巨佬的)
先tarjan缩点,把每一个环都给建出一个方点,然后圆点和方点之间的边的权值为这个圆点到环上深度最小的点的最短距离(因为这是个环所以两边走的距离不一样的),圆圆之间的边权就是原图的边权
然后把圆方树给树剖了,和正常的求距离一样先求LCA
如果LCA是个圆点,那么直接求距离便是
如果LCA是个方点,那么说明这两个点的LCA在环上,设两个点为\(u,v\),\(u\)往上跳的时候跳到的第一个环上的点为\(uu\),\(v\)跳到的为\(vv\),那么最短路的组成就是\(dis(u,uu)+dis(uu,vv)+dis(vv,v)\)
因为按我们之前的连边方式,\(dis(u,uu)\)和\(dis(v,vv)\)都好求。现在问题就是怎么求解\(dis(uu,vv)\)了。我们考虑之前跑出来的这个点都环上深度最小的最短距离,记录一下这个距离是否经过返祖边,是的话用环长减去这个距离就是从另一边走的距离。那么这两个点的最短路也可以求了
然后考虑怎么跳到环上?首先环上那个点肯定是环代表的方点的儿子,那么如果是重儿子,就是它的dfs序+1的点,否则的话它就是一条重链的起点并且是LCA的某个儿子,记录一下每一次跳的重链的起点便是
//minamoto#include#define ll long long#define fp(i,a,b) for(register int i=a,I=b+1;i I;--i)#define go(T,u) for(register int i=T.head[u],v=T.e[i].v;i;i=T.e[i].nx,v=T.e[i].v)#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)inline int max(const int &x,const int &y){return x>y?x:y;}inline int min(const int &x,const int &y){return x '9'||ch<'0')(ch=='-')&&(f=-1); for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0'); return res*f;}char sr[1<<21],z[20];int C=-1,Z=0;inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}void print(int x){ if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x; while(z[++Z]=x%10+48,x/=10); while(sr[++C]=z[Z],--Z);sr[++C]='\n';}const int N=100005;struct eg{int v,nx,w;};struct TR{ eg e[N];int head[N],tot; inline void add(int u,int v,int w){ e[++tot]={v,head[u],w},head[u]=tot; e[++tot]={u,head[v],w},head[v]=tot; }}T,G;int n;struct sl{ int fa[N],sz[N],son[N],top[N],dep[N],dis[N]; int dfn[N],rk[N],cir[N],tim;bool vis[N]; void dfs1(int u){ sz[u]=1,dep[u]=dep[fa[u]]+1; go(T,u)if(v!=fa[u]){ fa[v]=u,dis[v]=dis[u]+T.e[i].w; dfs1(v),sz[u]+=sz[v]; if(sz[v]>sz[son[u]])son[u]=v; } } void dfs2(int u,int t){ top[u]=t,dfn[u]=++tim,rk[tim]=u;if(!son[u])return; dfs2(son[u],t); go(T,u)if(v!=fa[u]&&v!=son[u])dfs2(v,v); } inline int LCA(int u,int v){ while(top[u]!=top[v])dep[top[u]]