• 【BZOJ2125】最短路(仙人掌,圆方树)


    【BZOJ2125】最短路(仙人掌,圆方树)

    题面

    BZOJ
    求仙人掌上两点间的最短路

    题解

    终于要构建圆方树啦
    首先构建出圆方树,因为是仙人掌,和一般图可以稍微的不一样
    直接(tarjan)缩点,对于每一个强连通分量构建方点(只有一个点的就不要建了)
    圆方边的权值定义为到(dfs)((Tarjan)不就是搞了一棵(dfs)树出来吗?)树上深度最小的点的最短距离。
    为什么会有最短距离?因为它是一个环啊,走两侧的距离是不同的。

    将圆方树树链剖分,和普通的求距离一样,先求解(LCA)
    如果(LCA)是圆点,那么和普通的树没有任何区别,直接求解
    如果是方点,那么意味这这两个点的祖先在一个环上
    因此,最短路要考虑这个环上这两个祖先的较小距离
    对于方点维护一下环的长度,记录一下每个点到达深度最小的点是否经过返祖边
    求距离时,首先跳到这两个环上的点,然后计算一下距离就好啦。
    怎么跳到环上?
    方案一:不用树链剖分了,我直接用倍增
    方案二:考虑树链剖分每个点只有一个重儿子,现在要求的是当前这个点到达(LCA)的所有祖先中,是(LCA)儿子的那个点。
    我们分类讨论一下,如果它是重儿子,那就是(LCA)(dfs)序的后面那个点。
    如果不是重儿子,那么它就是一条重链的起点,并且他的父亲是(LCA)
    既然这样,沿着重链跳就好啦

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define RG register
    #define MAX 20000
    inline int read()
    {
        RG int x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    struct Line{int v,next,w;};
    struct Link
    {
    	Line e[111111];
    	int h[MAX],cnt;
    	inline void Add(int u,int v,int w)
    	{
    		e[++cnt]=(Line){v,h[u],w};h[u]=cnt;
    		e[++cnt]=(Line){u,h[v],w};h[v]=cnt;
    	}
    }T,G;
    int n;
    struct RST
    {
    	int fa[MAX],size[MAX],hson[MAX],top[MAX],dep[MAX],dis[MAX];
    	int dfn[MAX],tim,ln[MAX],cir[MAX];
    	bool zn[MAX];
    	void dfs1(int u,int ff)
    	{
    		fa[u]=ff;size[u]=1;dep[u]=dep[ff]+1;
    		for(int i=T.h[u];i;i=T.e[i].next)
    		{
    			int v=T.e[i].v;if(v==ff)continue;
    			dis[v]=dis[u]+T.e[i].w;
    			dfs1(v,u);size[u]+=size[v];
    			if(size[v]>size[hson[u]])hson[u]=v;
    		}
    	}
    	void dfs2(int u,int tp)
    	{
    		top[u]=tp;dfn[u]=++tim,ln[tim]=u;
    		if(hson[u])dfs2(hson[u],tp);
    		for(int i=T.h[u];i;i=T.e[i].next)
    			if(T.e[i].v!=fa[u]&&T.e[i].v!=hson[u])
    				dfs2(T.e[i].v,T.e[i].v);
    	}
    	int LCA(int u,int v)
    	{
    		while(top[u]^top[v])dep[top[u]]<dep[top[v]]?v=fa[top[v]]:u=fa[top[u]];
    		return dep[u]<dep[v]?u:v;
    	}
    	int Jump(int u,int LCA)
    	{
    		int ret;
    		while(top[u]!=top[LCA])
    			ret=top[u],u=fa[top[u]];
    		return u==LCA?ret:ln[dfn[LCA]+1];
    	}
    	int Query(int u,int v)
    	{
    		int lca=LCA(u,v);
    		if(lca<=n)return dis[u]+dis[v]-2*dis[lca];
    		int uu=Jump(u,lca),vv=Jump(v,lca);
    		int d1=dis[uu]-dis[lca],d2=dis[vv]-dis[lca];
    		if(!zn[uu])d1=cir[lca]-d1;if(!zn[vv])d2=cir[lca]-d2;
    		return dis[u]-dis[uu]+dis[v]-dis[vv]+min(abs(d1-d2),cir[lca]-abs(d1-d2));
    	}
    }RST;
    int dfn[MAX],low[MAX],tim,tp[MAX],dep[MAX];
    int fa[MAX];
    ll dis[MAX];
    int S[MAX],tot,m,Q;
    void Build(int u,int y,int d)
    {
    	int top=dep[y]-dep[u]+1,sum=d,Dis=0;
    	for(int i=y;i!=u;i=fa[i])S[top--]=i,sum+=dis[i]-dis[fa[i]];
    	++tot;S[1]=u;top=dep[y]-dep[u]+1;RST.cir[tot]=sum;
    	for(int i=1;i<=top;++i)
    	{
    		int D=min(Dis,sum-Dis);
    		T.Add(tot,S[i],D);
    		RST.zn[S[i]]=(D==Dis);
    		Dis+=dis[S[i+1]]-dis[S[i]];
    	}
    }
    void Tarjan(int u,int ff)
    {
    	dfn[u]=low[u]=++tim;dep[u]=dep[ff]+1;fa[u]=ff;
    	for(int i=G.h[u];i;i=G.e[i].next)
    	{
    		int v=G.e[i].v;if(v==ff)continue;
    		if(!dfn[v])
    		{
    			dis[v]=dis[u]+G.e[i].w;
    			Tarjan(v,u);low[u]=min(low[u],low[v]);
    		}
    		else low[u]=min(low[u],dfn[v]);
    		if(dfn[u]<low[v])T.Add(u,v,G.e[i].w);
    	}
    	for(int i=G.h[u];i;i=G.e[i].next)
    	{
    		int v=G.e[i].v;if(v==ff)continue;
    		if(fa[v]!=u&&dfn[u]<dfn[v])Build(u,v,G.e[i].w);
    	}
    }
    int main()
    {
    	tot=n=read();m=read();Q=read();G.cnt=1;
    	for(int i=1;i<=m;++i)
    	{
    		int u=read(),v=read(),w=read();
    		G.Add(u,v,w);
    	}
    	Tarjan(1,0);
    	RST.dfs1(1,0);RST.dfs2(1,1);
    	while(Q--)printf("%d
    ",RST.Query(read(),read()));
    	return 0;
    }
    
    
  • 相关阅读:
    求集合的幂集【转】
    poj 1597 Uniform Generator【生成指定范围内所有随机数】
    OpenJudge计算概论-字符串排序
    OpenJudge计算概论-单词替换
    wikioi 1430 素数判定
    OpenJudge计算概论-字符串最大跨距
    wikioi 1012最大公约数和最小公倍数【根据最大公约数和最小公倍数求原来的两个数a、b】
    OpenJudge就算概论-最长单词2【寻找句子内部最长的单词】
    OpenJudge就算概论-统计字符数
    OpenJudge就算概论-过滤多余的空格
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9097299.html
Copyright © 2020-2023  润新知