Luogu4886 快递员 点分治

传送门


淀粉质好题啊qaq

我们先考虑随便选择一个点作为邮递中心,通过移动邮递中心找到更优的位置。将路径最大值求出,并将路径最大值对应的那一些路径拿出来考虑。可以知道,如果说这些路径中存在一条经过当前邮递中心的路径,意味着当前点就是最优的(因为不论邮递中心怎么移动,这一条路径的长度不会小于当前值,也就是说答案不会小于当前的最大值),所以只有起点和终点在同一子树内的路径的最长路径才有可能通过移动邮递中心使得答案变得更小。而如果说存在两条路径分布在不同子树内,显然也是无法通过移动使得答案更优的,因为不论邮递中心怎么移动,至少会有一条路径的长度增加。所以只有所有最长路径都在当前邮递中心的一棵子树内的时候,可以通过将邮递中心向子树内移动获得更优的答案。

但是显然每一次都移动一格的复杂度最坏是$O(nm)$的,我们考虑优化。我们可以借助于点分治的思想,每一次移动不是移动到子树的根的位置,而是跳到这一棵子树的重心的位置递归求解,这样的复杂度就是$O(mlogn)$了。

  1 #include<bits/stdc++.h>
  2 //This code is written by Itst
  3 using namespace std;
  4 
  5 inline int read(){
  6     int a = 0;
  7     bool f = 0;
  8     char c = getchar();
  9     while(c != EOF && !isdigit(c)){
 10         if(c == '-')
 11             f = 1;
 12         c = getchar();
 13     }
 14     while(c != EOF && isdigit(c)){
 15         a = (a << 3) + (a << 1) + (c ^ '0');
 16         c = getchar();
 17     }
 18     return f ? -a : a;
 19 }
 20 
 21 const int MAXN = 100010;
 22 struct Edge{
 23     int end , upEd , w;
 24 }Ed[MAXN << 1];
 25 int head[MAXN] , query[MAXN][2] , dis[MAXN] , size[MAXN] , be[MAXN];
 26 int N , M , cntEd , ans , nowSize , minSize , minInd;
 27 bool vis[MAXN];
 28 
 29 inline void addEd(int a , int b , int c){
 30     Ed[++cntEd].end = b;
 31     Ed[cntEd].upEd = head[a];
 32     Ed[cntEd].w = c;
 33     head[a] = cntEd;
 34 }
 35 
 36 void getSize(int now){
 37     ++nowSize;
 38     vis[now] = 1;
 39     for(int i = head[now] ; i ; i = Ed[i].upEd)
 40         if(!vis[Ed[i].end])
 41             getSize(Ed[i].end);
 42     vis[now] = 0;
 43 }
 44 
 45 void getRoot(int now){
 46     int maxN = 0;
 47     size[now] = vis[now] = 1;
 48     for(int i = head[now] ; i ; i = Ed[i].upEd)
 49         if(!vis[Ed[i].end]){
 50             getRoot(Ed[i].end);
 51             size[now] += size[Ed[i].end];
 52             maxN = max(maxN , size[Ed[i].end]);
 53         }
 54     maxN = max(maxN , nowSize - size[now]);
 55     if(maxN < minSize){
 56         minSize = maxN;
 57         minInd = now;
 58     }
 59     vis[now] = 0;
 60 }
 61 
 62 void getDis(int now , int fa , int cnt , int len){
 63     be[now] = cnt;
 64     dis[now] = len;
 65     for(int i = head[now] ; i ; i = Ed[i].upEd)
 66         if(Ed[i].end != fa)
 67             getDis(Ed[i].end , now , cnt , len + Ed[i].w);
 68 }
 69 
 70 void solve(int now){
 71     nowSize = 0;
 72     minSize = 0x7fffffff;
 73     getSize(now);
 74     getRoot(now);
 75     int root = minInd , cnt = 0 , maxL = 0 , allBe = 0;
 76     vis[root] = 1;
 77     dis[root] = be[root] = 0;
 78     for(int i = head[root] ; i ; i = Ed[i].upEd)
 79         getDis(Ed[i].end , root , ++cnt , Ed[i].w);
 80     for(int i = 1 ; i <= M ; ++i)
 81         maxL = max(maxL , dis[query[i][0]] + dis[query[i][1]]);
 82     ans = min(ans , maxL);
 83     for(int i = 1 ; i <= M ; ++i)
 84         if(dis[query[i][0]] + dis[query[i][1]] == maxL)
 85             if(be[query[i][0]] == be[query[i][1]] && (!allBe || allBe == be[query[i][0]]))
 86                 allBe = be[query[i][0]];
 87             else
 88                 if((!be[query[i][0]] || !be[query[i][1]]) && (!allBe || allBe == be[query[i][0]] + query[i][1]))
 89                     allBe = be[query[i][0]] + be[query[i][1]];
 90                 else
 91                     return;
 92     for(int i = head[root] ; i ; i = Ed[i].upEd)
 93         if(!vis[Ed[i].end] && be[Ed[i].end] == allBe){
 94             solve(Ed[i].end);
 95             return;
 96         }
 97 }
 98 
 99 int main(){
100     N = read();
101     M = read();
102     ans = 2147483647;
103     for(int i = 1 ; i < N ; ++i){
104         int a = read() , b = read() , c = read();
105         addEd(a , b , c);
106         addEd(b , a , c);
107     }
108     for(int i = 1 ; i <= M ; ++i){
109         query[i][0] = read();
110         query[i][1] = read();
111     }
112     solve(1);
113     printf("%d" , ans);
114     return 0;
115 }

猜你喜欢

转载自www.cnblogs.com/Itst/p/10074814.html
今日推荐