Codeforces519 E. A and B and Lecture Rooms


傳送門:>Here<

題意:詢問給出一棵無根樹上任意兩點$a,b$,求關於所有點$i$,$dist(a,i) = dist(b,i)$的點的數量。要求每一次詢問在$O(log n)$的時間復雜度內完成。

解題思路

由於在樹上求距離,並且還要$O(log n)$,自然會聯想到$LCA$。由於邊權是$1$,那么點到根的距離就是該點的深度。這個深度可以在$dfs$預處理的過程中處理完成。那么兩個點之間的距離就是兩個點到根節點的距離減去兩點的LCA到根節點距離的兩倍。這個隨便yy一下就好了。

得到$a,b$間的距離$D$以后,分類討論。(設$a$的深度$\geq \ b$的深度)

(1)若$D$為奇數,則一定不存在任何一個點到$a,b$的距離相等。因此得到$0$.

(2)若$D$為偶數:

 (一)$a,b$兩點分別在$LCA$的兩棵子樹上。

①$a,b$兩點深度相同。此時很簡單,最近的一個距離相等的點就是$a,b$的$LCA$。也很容易想到$LCA$的祖先也全都符合。但真的只有這些嗎?$LCA$的祖先的其他兒子好像也滿足誒……$LCA$的其他子樹(除了$a,b$)好像也滿足誒……因此我們得到結論,在這種情況下得到的答案應當是$n - size[LCA的左子樹] - size[LCA的右子樹]$

②深度不同。那么我們找到中間節點$Mid$,$Mid$里除有$a$的子樹外其他子樹都符合,並且$Mid$以上的節點都不會符合,因此答案是$size[Mid] - size[有a的那棵子樹]$

(二)$a,b$在同一條鏈上,即$b$就是$LCA$

和①類似,中間深度的節點減去含$a$的子樹即可

因此我們要做的不過是在$dfs$的過程中維護好$size$和$dep$。但一直困惑我的是有$a$的那個子樹怎么快速得到?答案其實很暴力……再倍增一遍……

Code

太坑了!調試了一個多小時竟然是因為$LCA$的預處理dfs中$(1<<i)$打成了$i$,導致$TLE$得莫名其妙。還是$LCA$板子不熟啊……

/** This Program is written by QiXingZhi **/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <cmath>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int N = 100010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
    if(c == '-') w = -1, c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
    return x * w;
}
int n,x,y,ans;
int f[N][30],dep[N],size[N];
vector <int> G[N];
inline void AddEdge(int u, int v){
    G[u].push_back(v);
}
void LcaInit(int x, int father, int _d){
    dep[x] = _d;
    f[x][0] = father;
    size[x] = 1;
    for(int i = 1; (1<<i) <= _d; ++i){
        f[x][i] = f[f[x][i-1]][i-1];
    }
    int sz,to;
    sz = G[x].size();
    for(int i = 0; i < sz; ++i){
        to = G[x][i];
        if(to == father) continue;
        LcaInit(to,x,_d+1);
        size[x] += size[to];
    }
}
inline int GetDepNode(int x, int _d){
    int tmp = x;
    for(int i = 25; i >= 0; --i){
        if(dep[tmp]-(1<<i) < _d) continue;
        tmp = f[tmp][i];
    }
    return tmp;
}
inline void LCA(int a, int b){
    if(dep[a] < dep[b]){
        swap(a,b);
    }
    int _a = a, _b = b;
    for(int i = 25; i >= 0; --i){
        if(dep[a]-(1<<i) < dep[b]) continue;
        a = f[a][i];
    }
    int LCA;
    if(a == b){
        LCA = a;
    }
    else{
        for(int i = 25; i >= 0; --i){
            if(f[a][i] == f[b][i]) continue;
            a = f[a][i];
            b = f[b][i];
        }
        LCA = f[a][0];
    }
    int Dist = dep[_a]-dep[LCA]+dep[_b]-dep[LCA];
    if(Dist & 1){
        ans = 0;
        return;
    }
    else{
        if(_b == LCA){
            int dep_Mid = (dep[_a] + dep[_b]) / 2;
            ans = size[GetDepNode(_a,dep_Mid)] - size[GetDepNode(_a,dep_Mid+1)];
        }
        else{
            if(dep[_a] != dep[_b]){
                int dep_Mid = dep[_a] - (Dist/2);
                ans = size[GetDepNode(_a,dep_Mid)] - size[GetDepNode(_a,dep_Mid+1)];
            }
            else{
                ans = n - size[GetDepNode(_a,dep[LCA]+1)] - size[GetDepNode(_b,dep[LCA]+1)];
            }
        }
    }
}
int main(){
    n = r;
    for(int i = 1; i < n; ++i){
        x = r, y = r;
        AddEdge(x,y);
        AddEdge(y,x);
    }
    LcaInit(1,0,1);
    int Q = r;
    while(Q--){
        x = r, y = r;
        ans = 0;
        if(x != y){
            LCA(x,y);
            printf("%d\n",ans);
        }
        else{
            printf("%d\n",n);
        }
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM