[递归][重心] Luogu P4886 快递员

题目描述

Bob 的城市里有 nn 个邮递站,由于经济考虑,这些邮递站被 n - 1n1 条带权无向边相连。即:这 nn 个邮递站构成了一棵树。

Bob 正在应聘一个快递员的工作,他需要送 mm 个商品,第 ii 个商品需要从 uu 送到 vv。由于 Bob 不能带着商品走太长的路,所以对于一次送货,他需要先从快递中心到 uu,再从 uu 回到快递中心,再从快递中心到 vv,最后从 vv返回快递中心。换句话说,如果设快递中心是 cc 号点,那么他的路径是 c \rightarrow u \rightarrow c \rightarrow v \rightarrow ccucvc。

现在 Bob 希望确定一个点作为快递中心,使得他送货所需的最长距离最小。显然,这个最长距离是个偶数,你只需要输出最长距离除以 22 的结果即可。

输入输出格式

输入格式:

第一行输入两个数 n, mn,m,意义如上。

接下来n-1n1行,每行三个数 u_i, v_i, w_iui,vi,wi,表示一条连接 u_i, v_iui,vi,长度为 w_iwi 的边。

接下来 mm 行,每行两个整数 u_i, v_iui,vi,表示商品的起止位置。

输出格式:

一行一个整数,表示答案。

输入输出样例

输入样例#1:
3 1
1 2 1
2 3 1
1 3
输出样例#1:
2

说明

对于 25\%25% 的数据,满足 1 \leq n, m \leq 10001n,m1000。

对于 100\%100% 的数据,满足 1 \leq n, m \leq 10^5, 1 \leq w_i \leq 10001n,m105,1wi1000。

题解

  • 首先随便选一个点,临时作为快递中心 

  • 然后可以O(n)计算出每一组点对到快递中心的距离和

  • 设点对到快递中心最大距离和为Max,可能有多个点对到快递中心的距离和都是Max那么就把它们都存下来

  • 如果当前的快递中心在任意一组距离最大的点对之间最短路径上,那么答案就是Max不可能再小了

  • 如果有任意两组点对,一组在当前快递中心的一棵子树里,另一组在另一棵子树里,那么答案同样也无法更小

  • 否则答案最优时的快递中心就有可能在当前唯一一棵有点对的子树中

  • 那么就取这棵子树的重心作为临时快递中心,然后递归

  • 由于每次取的都是重心,所以只会递归logn层,总时间复杂度为O(nlogn)

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 using namespace std;
 6 const int N=100010;
 7 int n,m,cnt,root,sum,ans=10000*N,u[N],v[N],head[N],size[N],mx[N],bz[N],dis[N],Q[N];
 8 bool vis[N];
 9 struct edge {int to,from,v;}e[N*2];
10 void insert(int x,int y,int v)
11 {
12     e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt,e[cnt].v=v;
13     e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt,e[cnt].v=v;
14 }
15 void getdis(int x,int fa,int y)
16 {
17     bz[x]=y;
18     for (int i=head[x];i;i=e[i].from) if (e[i].to!=fa) dis[e[i].to]=dis[x]+e[i].v,getdis(e[i].to,x,y);
19 }
20 void getroot(int x,int fa)
21 {
22     size[x]=1,mx[x]=0; 
23     for (int i=head[x];i;i=e[i].from) if (!vis[e[i].to]&&e[i].to!=fa) 
24     {
25         getroot(e[i].to,x),size[x]+=size[e[i].to];
26         if (size[e[i].to]>mx[x]) mx[x]=size[e[i].to];
27     }
28     mx[x]=max(mx[x],sum-mx[x]);
29     if (mx[x]<mx[root]) root=x;
30 }
31 void solve(int x)
32 {
33     if (vis[x]) { printf("%d",ans); exit(0); }
34     vis[x]=true,dis[x]=0;
35     for (int i=head[x];i;i=e[i].from) dis[e[i].to]=e[i].v,getdis(e[i].to,x,e[i].to);
36     int Mx=0,len=0,ls=0;
37     for (int i=1;i<=m;i++) if (dis[u[i]]+dis[v[i]]>Mx) Q[len=1]=i,Mx=dis[u[i]]+dis[v[i]]; else if (dis[u[i]]+dis[v[i]]==Mx) Q[++len]=i; 
38     ans=min(ans,Mx);
39     for (int i=1;i<=len;i++)
40     {
41         if (bz[u[Q[i]]]!=bz[v[Q[i]]]) { printf("%d",ans); exit(0); }
42         if (!ls) ls=bz[u[Q[i]]];
43         if (bz[u[Q[i]]]!=ls) { printf("%d",ans); exit(0); }
44     }
45     root=0,sum=size[ls],getroot(ls,0),solve(root);
46 }
47 int main()
48 {
49     scanf("%d%d",&n,&m);
50     for (int i=1,x,y,z;i<n;i++) scanf("%d%d%d",&x,&y,&z),insert(x,y,z);
51     for (int i=1;i<=m;i++) scanf("%d%d",&u[i],&v[i]);
52     sum=mx[0]=n,getroot(1,0),solve(root),printf("%d",ans);
53 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/11198817.html