[bzoj2125]最短路——仙人掌,圆方树

题目大意

求仙人掌上最短路.

思路

将仙人掌上的所有环给建立方点,所有环上的点作为圆点连在方点上面.

考虑一个以1为根的树型结构,我们将所有环上的点和方点的距离设为该点离环上深度最小的点的最小距离.

这样利用树上倍增来求解两点之间距离后,我们发现跨过的环(方点)上的路程就是环上的点离环上深度最小的点的最小距离,于是我们只需要判断一下lca是否是方点即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.3.3
 * Problem : luogu5263
 * E-mail : ylsoi@foxmail.com
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu5263.in","r",stdin);
    freopen("luogu5263.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

const int maxn=10000+10;
const int maxm=20000+10;
int n,m,q;
int beg[maxn],to[maxm<<1],las[maxm<<1],w[maxm<<1],cnte=1;
int dfn[maxn],low[maxn],cnt_dfn,pre[maxn];
int tot,anc[maxn<<1][21],dep[maxn<<1],Log[maxn<<1];
ll val[maxn<<1][21],ss[maxn<<1],ssum[maxn<<1];
vector<int>G[maxn<<1];

void add(int u,int v,int ww){
    las[++cnte]=beg[u],beg[u]=cnte,to[cnte]=v,w[cnte]=ww;
    las[++cnte]=beg[v],beg[v]=cnte,to[cnte]=u,w[cnte]=ww;
}

void tarjan(int u,int fe){
    dfn[u]=low[u]=++cnt_dfn;
    for(int i=beg[u];i;i=las[i]){
        if((i^1)==fe)continue;
        int v=to[i];
        if(!dfn[v]){
            pre[v]=i;
            tarjan(v,i);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])G[u].pb(v),val[v][0]=w[i];
        }
        else low[u]=min(low[u],dfn[v]);
    }
    for(int i=beg[u];i;i=las[i]){
        if((i^1)==fe)continue;
        int v=to[i];
        if(pre[v]!=i && dfn[v]>dfn[u]){
            ll sum=w[i],s=w[i];
            for(int p=v;p!=u;p=to[pre[p]^1])
                sum+=w[pre[p]];
            ++tot;
            G[u].pb(tot);
            for(int p=v;p!=u;p=to[pre[p]^1]){
                G[tot].pb(p);
                ssum[p]=sum,ss[p]=s;
                val[p][0]=min(s,sum-s),s+=w[pre[p]];
            }
        }
    }
}

void init(){
    read(n),read(m),read(q);
    int u,v,ww;
    REP(i,1,m){
        read(u),read(v),read(ww);
        add(u,v,ww);
    }
    REP(i,2,n<<1)Log[i]=Log[i>>1]+1;
}

void dfs(int u,int fh){
    anc[u][0]=fh,dep[u]=dep[fh]+1;
    REP(i,0,G[u].size()-1){
        int v=G[u][i];
        dfs(v,u);
    }
}

ll solve(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    ll ret=0;
    for(int d=Log[dep[x]-dep[y]];dep[x]!=dep[y];--d){
        if(dep[anc[x][d]]>=dep[y])ret+=val[x][d],x=anc[x][d];
    }
    if(x==y)return ret;
    for(int d=Log[dep[x]];d>=0;--d)
        if(anc[x][d]!=anc[y][d]){
            ret+=val[x][d],x=anc[x][d];
            ret+=val[y][d],y=anc[y][d];
        }
    if(anc[x][0]>n){
        return ret+min(abs(ss[x]-ss[y]),ssum[x]-abs(ss[x]-ss[y]));
    }
    else{
        return ret+val[x][0]+val[y][0];
    }
}

void work(){
    tot=n;
    tarjan(1,0);
    dfs(1,0);
    REP(j,1,15)REP(i,1,tot)
        if((1<<j)<=dep[i]-1){
            anc[i][j]=anc[anc[i][j-1]][j-1];
            val[i][j]=val[i][j-1]+val[anc[i][j-1]][j-1];
        }
    int u,v;
    REP(i,1,q){
        read(u),read(v);
        printf("%lld\n",solve(u,v));
    }
}

int main(){
    File();
    init();
    work();
    return 0;
}
posted @ 2019-03-03 20:17  ylsoi  阅读(...)  评论(...编辑  收藏