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

转载于:https://www.cnblogs.com/ylsoi/p/10467319.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值