#6145. 「2017 山東三輪集訓 Day7」Easy 動態點分治
阿新 • • 發佈:2019-01-02
\(\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;
}