P4886 快递员

P4886 快递员

0x01 题意

给定一棵树和树上若干个点对,选出一个点(x)最小化(max_{1leq ileq m}(dis(u_i,x)+dis(v_i,s)))

0x02 解

我们先随便选取一个点,找到所有使得题给式子值最大的点对;

接着有三种情况:

  1. 若该点在任意一个找到的点对的最短路径上,那么答案不能更优,输出该点
  2. 若找出的点对中任意两个点对在该点的子树中且不在同一子树中,那么答案不能更优,输出该点
  3. 若不满足以上两条,找到该点的子树中唯一有点对的那棵,取它的重心作为新的点进行递归

最后能够找到答案

0x03 码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
using namespace std;
const int INF=0x3f3f3f3f,N=500010;

int n,m,e[N],ne[N],w[N],idx,h[N],sum,siz[N],maxp[N],u[N],v[N],rt,sub[N],dist[N],que[N],ans=1e9;
bool vis[N];

inline void add(int a,int b,int c){
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

void groot(int now,int fa){//找重心
    siz[now]=1,maxp[now]=0;
    for(int i=h[now];~i;i=ne[i]){
        int j=e[i];
        if(vis[j]||j==fa) continue;
        groot(j,now);siz[now]+=siz[j];
        if(siz[j]>maxp[now]) maxp[now]=siz[j];
    }
    if(sum-siz[now]>maxp[now]) maxp[now]=sum-siz[now];
    if(maxp[now]<maxp[rt]) rt=now;
}

void gdist(int now,int fa,int st){//找到st结点的距离
    sub[now]=st;
    for(int i=h[now];~i;i=ne[i]){
        int j=e[i];
        if(j!=fa) dist[j]=dist[now]+w[i],gdist(j,now,st);
    }
}

void solve(int now){
    if(vis[now]){printf("%d
",ans);exit(0);}
    vis[now]=1,dist[now]=0;
    for(int i=h[now];~i;i=ne[i]){//预处理与now结点的距离
        int j=e[i];
        dist[j]=w[i];
        gdist(j,now,j);
    }
    int maxx=0,len=0,las=0;
    for(int i=1;i<=m;i++){
        if(dist[u[i]]+dist[v[i]]>maxx){//找与点对的最大距离
            len=1;
            que[len]=i;
            maxx=dist[u[i]]+dist[v[i]];
        }else if(dist[u[i]]+dist[v[i]]==maxx) que[++len]=i;//相等要加到队列里
    }
    if(maxx<ans) ans=maxx;//更新答案
    for(int i=1;i<=len;i++){
        if(sub[u[que[i]]]!=sub[v[que[i]]]){//在不在路径上
            printf("%d
",ans);
            exit(0);
        }
        if(!las) las=sub[u[que[i]]];//如果不在路径上,那就在子树里,记录下
        if(sub[u[que[i]]]!=las){//在不在同一子树里
            printf("%d
",ans);
            exit(0);
        }
    }
    rt=0,sum=siz[las],groot(las,0),solve(rt);//递归
}

int main(){

    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u[i],&v[i]);
    }
    sum=maxp[0]=n;
    groot(1,0);
    solve(rt);
    printf("%d
",ans);

    return 0;


    return 0;
}
原文地址:https://www.cnblogs.com/wsyunine/p/14802601.html