[题解] P4886 快递员

· · 题解

[题解] P4886 快递员

传送门

前缀部分

前缀知识:点分治。

对于此题而言,与其说是点分治的题,不如说是利用了点分治的递归思想,因此如果你没有学过点分治也没问题。

题解

更好的阅读体验

答案最优的情况(下面的“点对”都是对于距离最长的点对而言)

如果出现了以上两种情况,直接输出当前的最小 ans 即可。

否则递归进入最大的点对所在的那棵子树。

细节部分

对于这道题来说,首先要明白它不是每一棵子树都要递归的。

所以就不排除下面这两种情况:

什么意思呢?

比如现在有两个节点 rt1,rt2

以这两个节点为根时,算法都表现出了递归向另一方的趋势,所以会导致 TLE。

这就是下面代码所说的细节。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
inline T read(){
    T x=0;char ch=getchar();bool fl=false;
    while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
    while(isdigit(ch)){
        x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
    }
    return fl?-x:x;
}
const int maxn = 1e5 + 100;
struct edge{
    int to,nxt,w;
}e[maxn<<1];
int head[maxn],cnt=0;
inline void link(int u,int v,int w){
    e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;e[cnt].w=w;
}
#define mp make_pair
const int INF = 0x3f3f3f3f;
int n,m,S,rt=0;
int mx[maxn],sz[maxn],de[maxn];
bool vis[maxn];
pair<int,int> p[maxn];
void find_rt(int u,int fa){
    sz[u]=1;mx[u]=0;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa || vis[v])continue;
        find_rt(v,u);
        sz[u]+=sz[v];
        mx[u]=max(mx[u],sz[v]);
    }
    mx[u]=max(mx[u],S-sz[u]);
    if(mx[rt]>mx[u])rt=u;
}
int col[maxn];
void dfs(int u,int fa,int c){
    col[u]=c;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa)continue;
        de[v]=de[u]+e[i].w;
        dfs(v,u,c);
    }
}
int ans=INF;
#include <vector>
#define Pair pair<int,int>
int s[maxn];
void solve(int u){
    if(vis[u]){
        printf("%d\n",ans);exit(0);
    }//细节
    vis[u]=true;de[u]=0;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        de[v]=e[i].w;
        dfs(v,u,v);
    }
    int la=0,maxx=0;
    s[0]=0;
    for(int i=1;i<=m;i++){
        if(de[p[i].first]+de[p[i].second]>maxx){
            s[0]=0;s[++s[0]]=i;maxx=de[p[i].first]+de[p[i].second];
        }
        else if(de[p[i].first]+de[p[i].second]==maxx){
            s[++s[0]]=i;
        }
    }
    ans=min(ans,maxx);
    for(int i=1;i<=s[0];i++){
        if(col[p[s[i]].first]!=col[p[s[i]].second]){
            printf("%d\n",ans);exit(0);
        }
        else if(!la)la=col[p[s[i]].first];
        else if(la!=col[p[s[i]].first]){
            printf("%d\n",ans);exit(0);
        }
    }
    mx[rt=0]=INF;S=sz[la];
    find_rt(la,0);
    solve(rt);
}
#define read() read<int>()
int main(){
    n=read();m=read();
    for(int i=1,u,v,w;i<n;i++){
        u=read();v=read();w=read();
        link(u,v,w);link(v,u,w);
    }
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        p[i]=mp(u,v);
    }
    S=n;mx[rt=0]=INF;
    find_rt(1,0);
    solve(rt);
    return 0;
}