[题解] P4886 快递员
[题解] P4886 快递员
前缀部分
前缀知识:点分治。
对于此题而言,与其说是点分治的题,不如说是利用了点分治的递归思想,因此如果你没有学过点分治也没问题。
题解
-
首先是树上路径问题,很自然的想到点分治。
-
题意不再赘述,但是本题的最高价值就在于思考如何才能取得答案最优。
答案最优的情况(下面的“点对”都是对于距离最长的点对而言)
- 第一种情况,存在一组点对使得他们两个之间的最短路径经过当前的根节点。
- 第二种情况,至少存在两组点对分别分布在不同的点对内。\(e.g.\),\((u1,v1)\in son1,(u2,v2)\in son2\)
如果出现了以上两种情况,直接输出当前的最小 \(ans\) 即可。
否则递归进入最大的点对所在的那棵子树。
细节部分
对于这道题来说,首先要明白它不是每一棵子树都要递归的。
所以就不排除下面这两种情况:
-
递归子树的时候,找到的 \(rt\) 可能已经被 solve 过了。
-
有可能存在两种情况,使得这两种情况下可以互相转换(互相递归)。
什么意思呢?
比如现在有两个节点 \(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;
}