codeforces 519E (LCA)

題目鏈接:點擊這裏

題意:給出一個樹,q個詢問,每次詢問給出兩個點(u,v);求和u,v距離相等的點的個數.

直接LCA求出(u,v)的距離,然後找到他們的中間點,直接從深度深的那個點網上倍增距離的一半,然後直接子樹size加加減減就好了.

#include <bits/stdc++.h>
#define Clear(x,y) memset (x,y,sizeof(x))
#define FOR(a,b,c) for (int a = b; a <= c; a++)
#define REP(a,b,c) for (int a = b; a >= c; a--)
#define fi first
#define se second
#define pii pair<int, int>
#define pli pair<long long, int>
#define pb push_back
#define mod 1000000007
using namespace std;
#define maxn 100005
#define maxm maxn<<1

struct node {
    int v, next;
}edge[maxm];
int n, q, head[maxn], cnt;
int qu[maxn][2];

//LCA
int fa[maxn][22], deep[maxn];//節點i第2^j個祖先 深度
int DEG;
bool vis[maxn];

void bfs (int root) {
    DEG = 20;
    queue <int> q;
    while (!q.empty ()) q.pop ();
    deep[root] = 0;
    fa[root][0] = root;
    q.push (root);
    memset (vis, 0, sizeof vis); vis[root] = 1;
    while (!q.empty ()) {
        int u = q.front (); q.pop ();
        for (int i = 1; i < DEG; i++) {
            fa[u][i] = fa[fa[u][i-1]][i-1];
        }
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].v;
            if (vis[v]) continue;
            deep[v] = deep[u]+1;
            fa[v][0] = u;
            q.push (v);
            vis[v] = 1;
        }
    }
}

int LCA (int u, int v) {
    if (deep[u] > deep[v])
        swap (u, v);
    int hu = deep[u], hv = deep[v];
    int tu = u, tv = v;
    for (int det = hv-hu, i = 0; det; det >>= 1, i++) if (det&1) {
        tv = fa[tv][i];
    }
    if (tu == tv)
        return tu;
    for (int i = DEG-1; i >= 0; i--) {
        if (fa[tu][i] == fa[tv][i])
            continue;
        tu = fa[tu][i];
        tv = fa[tv][i];
    }
    return fa[tu][0];
}

int num[maxn];//子樹的size
void dfs (int u, int father) {
    num[u] = 1;
    for (int i = head[u]; i+1; i = edge[i].next) {
        int v = edge[i].v; if (v == father) continue;
        dfs (v, u);
        num[u] += num[v];
    }
}

int find (int u, int len) {
    int tmp = 0;
    while ((1<<(tmp+1)) <= len) tmp++;
    while (len) {
        while ((1<<tmp) > len) tmp--;
        u = fa[u][tmp];
        len -= (1<<tmp);
    }
    return u;
}

void add_edge (int u, int v) {
    edge[cnt].v = v, edge[cnt].next = head[u], head[u] = cnt++;
}

int main () {
    //freopen ("more.in", "r", stdin);
    cin >> n;
    Clear (head, -1);
    cnt = 0;
    for (int i = 1; i < n; i++) {
        int u, v; scanf ("%d%d", &u, &v);
        add_edge (u, v);
        add_edge (v, u);
    }
    bfs (1);
    dfs (1, 0);//算出每個子樹的規模
    cin >> q;
    for (int i = 1; i <= q; i++) {
        int u, v; scanf ("%d%d", &u, &v);
        if (u == v) {
            printf ("%d\n", n);
            continue;
        }
        int lca = LCA (u, v);
        if (deep[u] < deep[v]) swap (u, v);
        int tot = (deep[u]+deep[v]-2*deep[lca]);
        if (tot&1) {
            printf ("0\n");
            continue;
        }
        int len = tot/2;
        int p1 = find (u, len-1);//找到u網上走tot-1長度的祖先
        int p2;
        if (deep[u] == deep[v]) {
            p2 = find (v, len-1);
            printf ("%d\n", n-num[p1]-num[p2]);
        }
        else { 
            int mid = fa[p1][0]; 
            printf ("%d\n", num[mid]-num[p1]);
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章