bzoj2125 最短路

题目描述:

bz

题解:

圆方树。

将仙人掌搞成圆方树后将设方圆边边权设为圆点到环的顶点的最短距离。

这样的话询问时求一下$lca$,然后讨论。

若$lca$是圆点,直接返回距离。

若$lca$是方点,讨论环上的两个点是在同一方向到顶点还是在不同方向到顶点。

然后代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 10050;
template<typename T>
inline void read(T&x)
{
    T f = 1,c = 0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();}
    x = f*c;
}
int n,m,q,hed[N],cnt=1;
struct EG
{
    int to,nxt;
    ll w;
}e[N<<2];
void ae(int f,int t,ll w)
{
    e[++cnt].to = t;
    e[cnt].nxt = hed[f];
    e[cnt].w = w;
    hed[f] = cnt;
}
ll c[N<<1],d[N];
int dfn[N],low[N],tim,dep[N<<1];
int fa[N],r[N],tr;
int ff[N<<1][20];
ll md[N<<1][20];
int typ[N<<1];
int get_d(int x){return dep[x]?dep[x]:dep[x]=get_d(ff[x][0])+1;}
void fl(int u,int f,ll w)
{
    ff[u][0] = f;
    md[u][0] = w;
}
void sol(int u,int v,ll w)
{
    r[tr=1] = u;
    ll sum = w;
    while(v!=u)sum+=d[v],r[++tr]=v,v=fa[v];
    int now = ++n;
    fl(now,u,0);
    ll dis = w;
    for(int i=2;i<=tr;i++)
    {
        fl(r[i],now,min(dis,sum-dis));
        typ[r[i]]=(dis<sum-dis);
        dis+=d[r[i]];
    }
    c[now] = sum;
}
void tarjan(int u,int pre)
{
    dfn[u] = low[u] = ++tim;
    for(int j=hed[u];j;j=e[j].nxt)
    {
        int to = e[j].to;
        if(j==pre)continue;
        if(!dfn[to])
        {
            fa[to] = u,d[to] = e[j].w;
            tarjan(to,j^1);
            low[u] = min(low[u],low[to]);
        }else    low[u] = min(low[u],dfn[to]);
        if(low[to]>dfn[u])fl(to,u,e[j].w);
    }
    for(int to,j=hed[u];j;j=e[j].nxt)
        if(fa[to=e[j].to]!=u&&dfn[to]>dfn[u])
            sol(u,to,e[j].w);
}
void init()
{
    for(int i=1;i<=n;i++)get_d(i);
    for(int k=1;(1<<k)<=n;k++)
        for(int i=1;i<=n;i++)
            ff[i][k]=ff[ff[i][k-1]][k-1],md[i][k]=md[i][k-1]+md[ff[i][k-1]][k-1];
}
int get_lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=18;i>=0;i--)
        if(dep[ff[x][i]]>=dep[y])x=ff[x][i];
    if(x==y)return x;
    for(int i=18;i>=0;i--)
        if(ff[x][i]!=ff[y][i])x=ff[x][i],y=ff[y][i];
    return ff[x][0];
}
ll get_dis(int x,int y)
{
    ll ret = 0;
    if(dep[x]<dep[y])swap(x,y);
    for(int i=18;i>=0;i--)
        if(dep[ff[x][i]]>=dep[y])ret+=md[x][i],x=ff[x][i];
    if(x==y)return ret;
    for(int i=18;i>=0;i--)
        if(ff[x][i]!=ff[y][i])ret+=md[x][i]+md[y][i],x=ff[x][i],y=ff[y][i];
    return ret+md[x][0]+md[y][0];
}
int get_son(int x,int y)
{
    for(int i=18;i>=0;i--)
        if(dep[ff[x][i]]>dep[y])x=ff[x][i];
    return x;
}
ll query(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    int z = get_lca(x,y);
    ll w = get_dis(x,y);
    if(!c[z])return w;
    x = get_son(x,z),y = get_son(y,z);
    ll ta = md[x][0],tb = md[y][0];
    ll tmp = (typ[x]==typ[y])?abs(ta-tb):(ta+tb);
    return w-ta-tb+min(tmp,c[z]-tmp);
}
int main()
{
    read(n),read(m),read(q);
    for(int f,t,w,i=1;i<=m;i++)
    {
        read(f),read(t),read(w);
        ae(f,t,w),ae(t,f,w);
    }
    dep[1]=1;
    tarjan(1,0);
    init();
    for(int x,y,i=1;i<=q;i++)
    {
        read(x),read(y);
        printf("%lld\n",query(x,y));
    }
    return 0;
}
View Code

 

posted @ 2019-05-06 12:52  LiGuanlin  阅读(205)  评论(0编辑  收藏  举报