CF418D Big Problems for Organizers 树的直径、ST表

题目传送门:http://codeforces.com/problemset/problem/418/D

大意:给出一棵有$N$个节点的树,所有树边边权为$1$,给出$M$次询问,每个询问给出$x,y$两个节点,求$max_{i=1}^N\{min\{dis_{i,x},dis_{i,y}\}\}$。$N,M \leq 10^5$


倍增+长链剖分这种东西才不会写$qwq$所以写个思维难度大一点的直径写法。

我们考虑将一条直径拎出来,这样就相当于链上挂了若干棵树。因为树的直径有一个很好的性质:树上距离任意一个节点最远的点必定是两直径端点之一,所以不会存在任何一种情况,某一个询问达到的最大距离的点与给出的其中一个节点在一棵挂在直径上的子树上,所以可以将问题简化。

考虑以下几种情况:

$1.x,y$在同一直径上子树内,深度较浅的点的范围就一定会覆盖直径,答案就是直径两端点到深度较浅点的长度的较大值

$2.$我们把两个节点对应路径上的中点求出来,如果中点与$x$或$y$在同一子树内,那么另一个点的范围必须会覆盖直径,与$1$情况类似

$3.$如果在步骤$2$中求出的中点在直径上,我们就可以沿着中点劈开成两个连通块(如果中点在直径的某个点上,随意将其划入任何一个连通块),两边求出来的较大值就是答案。考虑如何求这个值。对于$x$控制的区域,在$x$子树对应的直径上的点左边的区域中,最大距离所在的点就是直径最左边的点,而对于其右边的直径上的点则与其连接子树的最大深度和到$x$所在直径点的距离有关,右边类似。这就是区间$RMQ$问题,可以使用$ST$表进行维护。时间复杂度为$O(NlogN+M)$

  1 #include<bits/stdc++.h>
  2 #define MAXN 100010
  3 using namespace std;
  4 
  5 inline int read(){
  6     int a = 0;
  7     char c = getchar();
  8     while(!isdigit(c))
  9         c = getchar();
 10     while(isdigit(c)){
 11         a = (a << 3) + (a << 1) + (c ^ '0');
 12         c = getchar();
 13     }
 14     return a;
 15 }
 16 
 17 struct Edge{
 18     int end , upEd;
 19 }Ed[MAXN << 1];
 20 int head[MAXN] , dep[MAXN] , maxDep[MAXN] , ST[MAXN][20][2] , bef[MAXN] , root[MAXN] , be[MAXN] , logg[MAXN];
 21 int N , cntEd , maxD , maxDir , cntRoot;
 22 bool isRoot[MAXN];
 23 
 24 inline void addEd(int a , int b){
 25     Ed[++cntEd].end = b;
 26     Ed[cntEd].upEd = head[a];
 27     head[a] = cntEd;
 28 }
 29 
 30 void dfsForZJ(int k , int fa , int dep){
 31     if(dep > maxD){
 32         maxD = dep;
 33         maxDir = k;
 34     }
 35     bef[k] = fa;
 36     for(int i = head[k] ; i ; i = Ed[i].upEd)
 37         if(Ed[i].end != fa)
 38             dfsForZJ(Ed[i].end , k , dep + 1);
 39 }
 40 
 41 inline void findZJ(int start , int end){
 42     isRoot[root[++cntRoot] = end] = 1;
 43     while(end != start)
 44         isRoot[root[++cntRoot] = end = bef[end]] = 1;
 45 }
 46 
 47 void dfsForDep(int k , int fa , int belong){
 48     be[k] = belong;
 49     for(int i = head[k] ; i ; i = Ed[i].upEd)
 50         if(!isRoot[Ed[i].end] && Ed[i].end != fa){
 51             maxDep[Ed[i].end] = dep[Ed[i].end] = dep[k] + 1; 
 52             dfsForDep(Ed[i].end , k , belong);
 53             maxDep[k] = max(maxDep[k] , maxDep[Ed[i].end]);
 54         }
 55 }
 56 
 57 void init(){
 58     for(int i = 1 ; i <= cntRoot ; i++){
 59         dfsForDep(root[i] , 0 , i);
 60         ST[i][0][0] = maxDep[root[i]] - i;
 61         ST[i][0][1] = maxDep[root[i]] + i;
 62     }
 63     for(int i = 1 ; 1 << i <= cntRoot ; i++)
 64         for(int j = 1 ; j + (1 << i) - 1 <= cntRoot ; j++){
 65             ST[j][i][0] = max(ST[j][i - 1][0] , ST[j + (1 << i - 1)][i - 1][0]);
 66             ST[j][i][1] = max(ST[j][i - 1][1] , ST[j + (1 << i - 1)][i - 1][1]);
 67         }
 68 }
 69 
 70 inline int query(int l , int r , int dir){
 71     if(l > r)
 72         return -0x3f3f3f3f;
 73     int t = logg[r - l + 1];
 74     return max(ST[l][t][dir] , ST[r - (1 << t) + 1][t][dir]);
 75 }
 76 
 77 int main(){
 78     N = read();
 79     for(int i = 2 ; i <= 100000 ; i++)
 80         logg[i] = logg[i >> 1] + 1;
 81     for(int i = 1 ; i < N ; i++){
 82         int a = read() , b = read();
 83         addEd(a , b);
 84         addEd(b , a);
 85     }
 86     dfsForZJ(1 , 0 , 1);
 87     maxD = 0; 
 88     int t = maxDir;
 89     dfsForZJ(t , 0 , 1);
 90     findZJ(t , maxDir);
 91     init();
 92     int M = read();
 93     long long lastans = 0;
 94     while(M--){
 95         int X = read() , Y = read();
 96         if(be[X] > be[Y])
 97             swap(X , Y);
 98         if(be[X] == be[Y])
 99             lastans = min(dep[X] , dep[Y]) + max(be[X] - 1 , cntRoot - be[X]);
100         else{
101             int t = be[X] + be[Y] + dep[Y] - dep[X];//这个算中点的方法比较迷诶……
102             if(t <= be[X] << 1)
103                 lastans = dep[Y] + max(be[Y] - 1 , cntRoot - be[Y]);
104             else
105                 if(t >= be[Y] << 1)
106                     lastans = dep[X] + max(be[X] - 1 , cntRoot - be[X]);
107                 else{
108                     t >>= 1;
109                     lastans = max(max(be[X] - 1 , query(be[X] + 1 , t , 1) - be[X]) + dep[X]
110                      , max(query(t + 1 , be[Y] - 1 , 0) + be[Y] , cntRoot - be[Y]) + dep[Y]);
111                 }
112         }
113         cout << lastans << endl;
114     }
115     return 0;
116 }

 

转载于:https://www.cnblogs.com/Itst/p/9767441.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值