三点通信

题目描述

这里写图片描述

输入

这里写图片描述

输出

这里写图片描述

样例输入

5 5 3
1 2 20
1 3 20
1 4 20
3 5 15
4 5 15
2 3 4
3 4 5
5 4 4

5 4 3
1 2 20
1 3 20
1 4 20
3 5 15
2 3 4
3 4 5
5 4 4

样例输出

60
30
15

60
55
55

提示

这里写图片描述

solution

首先考虑一棵树的情况
对于x,y,z三个点
想到先求x,y到LCA(x,y)的距离,再加上LCA(x,y)到z的距离
可是,马上就会发现会有边重复计算。不过好像换y,z或x,z先算就可以了。所以,三种情况都算一下,求个min就可以了。
再来看看n=m的情况,是在一棵树上加了一条边,形成一个环。这是一个基环外向树。
以环上的每一个点为根,形成一些树
考虑三种情况,
1、若x,y,z在同一棵树上,就按照一棵树的情况做
2、若x,y,z有两个点在同一棵树上,另一点在另一棵树上,设x,y在同一棵树上,先求出x,y到LCA(x,y)的距离,再加上LCA(x,y)到root[x]的距离和z到root[z]的距离,最后还有root[x]到root[z]的最短距离(用前缀和处理一下)
3、若x,y,z都不在一棵树上,求出每一个点到根的距离,再加上三个根之间的最短距离

这样子就可以AC了

code:

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
bool is_bridge[200005];
int belong[100005],sum[1000000],vis[100005],id[100005],jh[100005],jhnum,fi,ti,dfn[100005],color[100005],low[100005],n,m,q,Next[200005],head[100005],vet[200005],w[200005],en,fa[100005][20],deep[100005],D[100005];
void addedge(int u,int v,long long val){
    vet[++en]=v;
    Next[en]=head[u];
    head[u]=en;
    w[en]=val;
}
void dfs(int u,int pre){
    fa[u][0]=pre;
    D[u]=D[pre]+1;
    for(int i=1;i<=16;i++)
        if(D[u]>=(1<<i))
            fa[u][i]=fa[fa[u][i-1]][i-1];
        else
            break;
    for(int i=head[u];i;i=Next[i]){
        int v=vet[i];
        if(v!=pre){
            deep[v]=deep[u]+w[i];
            dfs(v,u);
        }
    }
}
int lca(int x,int y){
    if(D[x]<D[y])
        swap(x,y);
    int z=D[x]-D[y];
    for(int i=0;i<=16;i++)
        if(z&(1<<i))
            x=fa[x][i];
    for(int i=16;i>=0;i--)
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];
            y=fa[y][i];
        }
    if(x==y)
        return x;
    return fa[x][0];
}
void tarjan(int u,int pre){
    dfn[u]=low[u]=++ti;
    color[u]=1;
    for(int i=head[u];i;i=Next[i]){
        int v=vet[i];
        if(!color[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
        }
        else
        if(v!=pre)
            low[u]=min(low[u],dfn[v]);
        if(low[v]>dfn[u]){
            is_bridge[i]=true;
            if(i&1)
                is_bridge[i+1]=true;
            else
                is_bridge[i-1]=true;
        }
    }
}
void Dfs(int u){
    jh[++jhnum]=u;
    id[u]=jhnum;
    vis[u]=1;
    for(int i=head[u];i;i=Next[i])
        if(!is_bridge[i]&&!vis[vet[i]])
            Dfs(vet[i]);
}
void DFs(int u,int pre){
    vis[u]++;
    for(int i=head[u];i;i=Next[i])
        if(!is_bridge[i]&&vis[vet[i]]<2&&vet[i]!=pre){
            sum[id[vet[i]]+jhnum*vis[vet[i]]]=sum[id[u]+jhnum*(vis[u]-1)]+w[i];
            DFs(vet[i],u);
        }
}
void DFS(int u,int pre,int now){
    belong[u]=now;
    fa[u][0]=pre;
    D[u]=D[pre]+1;
    for(int i=1;i<=16;i++)
        if(D[u]>=(1<<i))
            fa[u][i]=fa[fa[u][i-1]][i-1];
        else
            break;
    for(int i=head[u];i;i=Next[i]){
        int v=vet[i];
        if(v!=pre&&!id[v]){
            deep[v]=deep[u]+w[i];
            DFS(v,u,now);
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z);
        addedge(y,x,z);
    }   
    if(m<n){
        D[1]=1;
        dfs(1,0);
        while(q--){
            int x,y,z,ans,xx,yy;
            scanf("%d%d%d",&x,&y,&z);
            xx=lca(x,y);
            yy=lca(xx,z);
            ans=-deep[yy]*2-deep[xx]+deep[z]+deep[x]+deep[y];
            xx=lca(x,z);
            yy=lca(xx,y);
            ans=min(ans,-deep[yy]*2-deep[xx]+deep[x]+deep[y]+deep[z]);
            xx=lca(y,z);
            yy=lca(x,yy);
            ans=min(ans,-deep[yy]*2-deep[xx]+deep[x]+deep[y]+deep[z]);
            printf("%d\n",ans);
        }
    }
    else{
        tarjan(1,0);
        for(int i=1;i<=n;i++)
            for(int j=head[i];j;j=Next[j])
                if(!is_bridge[j]){
                    fi=i;
                    goto ok;
                }
        ok:;
        memset(vis,0,sizeof(vis));
        Dfs(fi);
        memset(vis,0,sizeof(vis));
        DFs(fi,0);
        for(int i=1;i<=jhnum;i++)
            DFS(jh[i],0,i);
        //printf("%d %d %d %d %d\n",sum[1],sum[2],sum[3],sum[4],sum[5]);
        while(q--){
            int x,y,z,ans=1000000000,xx,yy,zz,add;
            scanf("%d%d%d",&x,&y,&z);
            //printf("%d %d %d\n",belong[x],belong[y],belong[z]);
            if(belong[x]==belong[y]&&belong[x]==belong[z]){     
                xx=lca(x,y);
                yy=lca(xx,z);
                ans=min(ans,-deep[yy]*2-deep[xx]+deep[x]+deep[y]+deep[z]);
                xx=lca(x,z);
                yy=lca(xx,y);
                ans=min(ans,-deep[yy]*2-deep[xx]+deep[x]+deep[y]+deep[z]);
                xx=lca(y,z);
                yy=lca(x,yy);
                ans=min(ans,-deep[yy]*2-deep[xx]+deep[x]+deep[y]+deep[z]);
            }
            else
            if(belong[x]==belong[y]){
                xx=lca(x,y);
                ans=deep[x]+deep[y]-deep[xx]+deep[z];
                xx=belong[x];
                yy=belong[z];
                if(xx>yy)
                    swap(xx,yy);
                ans+=min(sum[yy]-sum[xx],sum[xx+jhnum]-sum[yy]);
            }
            else
            if(belong[x]==belong[z]){
                xx=lca(x,z);
                ans=deep[x]+deep[y]-deep[xx]+deep[z];
                xx=belong[x];
                yy=belong[y];
                if(xx>yy)
                    swap(xx,yy);
                ans+=min(sum[yy]-sum[xx],sum[xx+jhnum]-sum[yy]);
            }
            else
            if(belong[y]==belong[z]){
                xx=lca(z,y);
                ans=deep[x]+deep[y]-deep[xx]+deep[z];
                xx=belong[x];
                yy=belong[z];
                if(xx>yy)
                    swap(xx,yy);
                ans+=min(sum[yy]-sum[xx],sum[xx+jhnum]-sum[yy]);
            }
            else{
                ans=deep[x]+deep[y]+deep[z];
                xx=belong[x];
                yy=belong[y];
                zz=belong[z];
                if(xx>yy)
                    swap(xx,yy);
                if(yy>zz)
                    swap(yy,zz);
                if(xx>yy)
                    swap(xx,yy);
                add=sum[zz]-sum[xx];
                add=min(add,sum[xx+jhnum]-sum[yy]);
                add=min(add,sum[yy+jhnum]-sum[zz]);
                ans+=add;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值