[题解] 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;
}
posted @ 2021-08-12 17:26  ¶凉笙  阅读(38)  评论(0编辑  收藏  举报