1. 程式人生 > >#6145. 「2017 山東三輪集訓 Day7」Easy 動態點分治

#6145. 「2017 山東三輪集訓 Day7」Easy 動態點分治

\(\color{#0066ff}{題目描述}\)

JOHNKRAM 最近在參加 C_SUNSHINE 舉辦的聚會。

C 國一共有 n 座城市,這些城市由 n−1 條無向道路連線。任意兩座城市之間有且僅有一條路徑。C_SUNSHINE 會在編號在 [1,n] 內的城市舉辦聚會。
為了整整 JOHNKRAM,C_SUNSHINE 把他丟在了城市 x,讓他自己走到一座城市去參加聚會。JOHNKRAM 希望你能幫他計算,他最少要走多長的路才能到達一座正在聚會的城市?
當然,C_SUNSHINE 一共舉行了 m 次聚會,所以 JOHNKRAM 也會詢問你 m 次。

\(\color{#0066ff}{輸入格式}\)

第一行包含一個整數 n,表示城市數量。
接下來 n - 1 行每行三個整數 u, v, d,表示一條無向道路的兩個端點和長度。
接下來一行包含一個整數 m,表示詢問個數。
接下來 m 行每行三個整數 l, r, x 表示一次詢問。
表示本次在[l,r](點的編號)的城市舉辦聚會

\(\color{#0066ff}{輸出格式}\)

對於每次詢問,輸出一行一個整數,表示詢問的答案。

\(\color{#0066ff}{輸入樣例}\)

3
1 2 1
1 3 1
3
2 3 1
2 3 2
3 3 2

\(\color{#0066ff}{輸出樣例}\)

1
0
2

\(\color{#0066ff}{資料範圍與提示}\)

對於 50% 的資料,\(n \leq 1000\)
對於 70% 的資料,保證樹是隨機生成的;
對於 100% 的資料,\(1 \leq n, m \leq 100000\),保證樹的直徑不超過 25000。

\(\color{#0066ff}{題解}\)

題意就是給你一棵有邊權的樹,m次詢問

詢問x到區間[l,r]內的點的最小值

動態點分治思想:建立點分樹(把所有樹的重心建成一棵樹(每個點都會成為重心,所以共n個點,可以證明高度\(\leq logn\)))

每個點所管轄的點就是點分樹中的子樹,維護一些資訊就行了

對於本題

點分樹的每個點維護一個動態開點的線段樹

x的線段樹中[l,r]代表點x到這個區間點的最小值

由於每個點管轄的不超過log個,所以在原圖上倍增求距離,可以把每個點的線段樹預處理出來\(O(nlog^2n)\)

對於每個詢問,在點分樹上向上跳(被管轄的點)

距離分為兩部分

一個是給出點到當前點的距離

一個是當前點到[l,r]的距離

前者用LCA, 後者線上段樹上直接查詢

#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL in() {
    char ch; int x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int inf = 0x7f7f7f7f;
const int maxn = 1e5 + 10;
int n;
struct sgt {
protected:
    struct node {
        int l, r;
        LL min;
        node *ch[2];
        node(int l = 0, int r = 0, LL min = inf): l(l), r(r), min(min) {
            ch[0] = ch[1] = NULL;
        }
        void *operator new (size_t) {
            static node *S = NULL, *T = NULL;
            return (S == T) && (T = (S = new node[1024]) + 1024), S++;
        }
        void upd() {
            min = inf;
            if(ch[0]) min = std::min(min, ch[0]->min);
            if(ch[1]) min = std::min(min, ch[1]->min);
        }
    };
    node *root;
    void add(node *&o, int l, int r, int pos, LL val) {
        if(o == NULL) o = new node(l, r, inf);
        if(l == r) return (void)(o->min = val);
        int mid = (l + r) >> 1;
        if(pos <= mid) add(o->ch[0], l, mid, pos, val);
        else add(o->ch[1], mid + 1, r, pos, val);
        o->upd();
    }
    LL query(node *o, int l, int r) {
        if(o == NULL) return inf;
        if(o->r < l || o->l > r) return inf;
        if(l <= o->l && o->r <= r) return o->min;
        return std::min(query(o->ch[0], l, r), query(o->ch[1], l, r));
    }
public:
    void add(int pos, int val) { add(root, 1, n, pos, val); }
    LL query(int l, int r) { return query(root, l, r); }
};
struct node {
    int to, dis;
    node *nxt;
    node(int to = 0, int dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
    void *operator new (size_t) {
        static node *S = NULL, *T = NULL;
        return (S == T) && (T = (S = new node[1024]) + 1024), S++;
    }
};
node *head[maxn];
int f[maxn], siz[maxn], sum, root;
int g[maxn][26], dep[maxn], fa[maxn];
LL d[maxn][26];
bool vis[maxn];
sgt t[maxn];
void add(int from, int to, int dis) {
    head[from] = new node(to, dis, head[from]);
}
void getroot(int x, int fa) {
    f[x] = 0, siz[x] = 1;
    for(node *i = head[x]; i; i = i->nxt) {
        if(i->to == fa || vis[i->to]) continue;
        getroot(i->to, x);
        siz[x] += siz[i->to];
        f[x] = std::max(f[x], siz[i->to]);
    }
    f[x] = std::max(f[x], sum - siz[x]);
    if(f[x] < f[root]) root = x;
}

void build(int x) {
    vis[x] = true;
    for(node *i = head[x]; i; i = i->nxt) {
        if(vis[i->to]) continue;
        root = 0, sum = siz[i->to];
        getroot(i->to, x);
        fa[root] = x;
        build(root);
    }
}
void dfs(int x, int fat) {
    g[x][0] = fat;
    dep[x] = dep[fat] + 1;
    for(node *i = head[x]; i; i = i->nxt)
        if(i->to != fat) dfs(i->to, x), d[i->to][0] = i->dis;
}
void beizeng() {
    dfs(1, 0);
    for(int j = 1; j <= 24; j++)
        for(int i = 1; i <= n; i++) {
            g[i][j] = g[g[i][j - 1]][j - 1];
            d[i][j] = d[i][j - 1] + d[g[i][j - 1]][j - 1];
        }
}
LL LCA(int x, int y) {
    if(dep[x] < dep[y]) std::swap(x, y);
    LL dis = 0;
    for(int i = 24; i >= 0; i--) if(dep[g[x][i]] >= dep[y]) dis += d[x][i], x = g[x][i];
    if(x == y) return dis;
    for(int i = 24; i >= 0; i--) 
        if(g[x][i] != g[y][i]) {
            dis += d[x][i] + d[y][i];
            x = g[x][i], y = g[y][i];
        }
    return dis + d[x][0] + d[y][0];
}
void predoit() {
    for(int i = 1; i <= n; i++) {
        int x = i;
        while(x) {
            t[x].add(i, LCA(x, i));
            x = fa[x];
        }
    }
}
void query() {
    for(int m = in(); m --> 0;) {
        int l = in(), r = in(), x = in();
        LL ans = inf;
        int y = x;
        while(y) {
            ans = std::min(ans, t[y].query(l, r) + LCA(x, y));
            y = fa[y];
        }
        printf("%lld\n", ans);
    }
}
int main() {
    n = in();
    int x, y, z;
    for(int i = 1; i < n; i++) {
        x = in(), y = in(), z = in();
        add(x, y, z), add(y, x, z);
    }
    f[0] = sum = n;
    getroot(1, 0);
    build(root);
    beizeng();
    predoit();
    query();
    return 0;
}