• 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;
    }
    
  • 相关阅读:
    C#里partial关键字的作用
    Xamarin.Android之布局文件智能提示问题
    C语言文件操作
    CArray
    Unicode和多字节的相互转换
    可变参数问题研究
    VC6.0支持UNICODE的步骤
    Unicode编码表
    @@
    内存映射文件原理
  • 原文地址:https://www.cnblogs.com/wsyunine/p/14802601.html
Copyright © 2020-2023  润新知