每次给一个点集, 求每个点到其他所有点的最大距离:
会修改边权;
修改边权之后,
我们可以用 dfs 序 + 线段树维护 当前点到根节点的距离.
还可以用树状数组 + 差分思想 维护. {
dfs 序之后,每个点都会有一个维护的区间,然后我们在这个区间里加上这个点保存的边权.
每个边 修改,我们修改这个区间. 区间开头 + , 结尾 - 就可以了;
最后直接查询一下就会是点到根节点的距离.
}
然后我们建虚树的时候,可以不每次都拿 1 当根节点,可以找一个 rt .
Top_xiao的犯错记录:
写这个题的时候,出现了几个问题:
1. 建完虚树的时候, 把原图 和 虚树的图弄混了,竟然 用 par[u][0] 当做 u 的父亲.
2. 然后 跑 f2 的时候,不能用 f[it] 因为 f[it] 用可能还没有更新到.因为是同一个父亲节点,直接用 f[u] 就可以.
3. 第一次以 1 为根节点建虚树的时候, 1 有可能并不是关键点,但是会被算进来. 所以算 f2 的时候需要特判一下.
4. 然后换了一种建虚树的方法, 并不是 以 1 当做根节点. 这个时候根节点的 f2 还要是 0, 还需要特判一下.
但是比 上一种 特判简单.
5. 最后 我的 dis 没有更新完全, 就是只更新了关键点,那些 lca 的非关键点没有更新.tmd.
#include<bits/stdc++.h>
#define ls now << 1
#define rs now << 1 | 1
using namespace std;
const int N = 1e5 + 100;
struct edge {
int u, v, w, next;
} g[N << 1];
vector<int>e[N];
int n, m, cnt, Head[N], st, stk[N], a[N], c[N], f[N], rt;
int L[N], R[N], dep[N], par[N][20], pre[N];
long long dis[N], val[N << 2], lz[N << 2], f1[N], f2[N], b[N];
void add(int u, int v, int w) {
++cnt;
g[cnt] = (edge) {
u, v, w, Head[u]
};
Head[u] = cnt;
}
bool cmp(const int &A, const int &B) {
return L[A] < L[B];
}
void pushdown(int now, int l, int r) {
if (lz[now] == 0) return;
val[ls] += lz[now];
val[rs] += lz[now];
lz[ls] += lz[now];
lz[rs] += lz[now];
lz[now] = 0;
}
void build(int now, int l, int r) {
if (l + 1 == r) {
val[now] = dis[pre[l]]; return;
}
int mid = (l + r) >> 1;
if (l < mid) build(ls, l, mid);
if (r >= mid) build(rs, mid, r);
val[now] = max(val[ls], val[rs]);
}
void modify(int now, int l, int r, int a, int b, long long k) {
if (a <= l && b >= r - 1) {
val[now] += k;
lz[now] += k;
return;
}
pushdown(now, l, r);
int mid = (l + r) >> 1;
if (a < mid) modify(ls, l, mid, a, b, k);
if (b >= mid) modify(rs, mid, r, a, b, k);
val[now] = max(val[ls], val[rs]);
}
long long Query(int now, int l, int r, int a, int b) {
if (l + 1 == r) return val[now];
pushdown(now, l, r);
int mid = (l + r) >> 1;
if (a < mid) return Query(ls, l, mid, a, b);
if (b >= mid) return Query(rs, mid, r, a, b);
return 0;
}
void dfs(int u, int fa) {
par[u][0] = fa;
dep[u] = dep[fa] + 1;
L[u] = ++L[0];
pre[L[0]] = u;
for (int i = 1; i < 20; ++i)
par[u][i] = par[par[u][i - 1]][i - 1];
for (int i = Head[u]; i; i = g[i].next) {
if (g[i].v == fa) continue;
b[g[i].v] = g[i].w;
dis[g[i].v] = dis[u] + 1ll * g[i].w;
dfs(g[i].v, u);
}
R[u] = L[0];
}
int LCA(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (int i = 19; i >= 0; --i)
if (dep[par[x][i]] >= dep[y]) x = par[x][i];
if (x == y) return x;
for (int i = 19; i >= 0; --i)
if (par[x][i] != par[y][i])
x = par[x][i], y = par[y][i];
return par[x][0];
}
void ins(int x) {
if (st == 0) {
stk[++st] = x;
return;
}
int t = LCA(x, stk[st]);
dis[t] = Query(1,1,n+1,L[t],L[t]);
if (t == stk[st]) {
stk[++st] = x;
return;
}
while(st > 1 && L[stk[st - 1]] >= L[t]) e[stk[st - 1]].push_back(stk[st]), st--;
if (t != stk[st]) e[t].push_back(stk[st]), stk[st] = t;
stk[++st] = x;
}
void get(int k) {
sort(a, a + k, cmp); st = 0;
for (int i = 0; i < k; ++i) ins(a[i]);
while(st > 1) e[stk[st - 1]].push_back(stk[st]), st--;
rt = stk[1];
}
void dfs1(int u) {
int len = e[u].size();
f1[u] = 0;
for (int i = 0; i < len; ++i) {
int v = e[u][i];
dfs1(v);
f1[u] = max(f1[u], f1[v] + dis[v] - dis[u]);
}
}
void dfs2(int u) {
int len = e[u].size();
int len1 = e[f[u]].size();
long long ans = 0;
for (int i = 0; i < len1; ++i) {
int it = e[f[u]][i];
if (it == u) continue;
ans = max(f1[it] + dis[it] - dis[f[u]], ans);
}
ans = max(ans, f2[f[u]]);
f2[u] = ans + dis[u] - dis[f[u]];
if (rt == u) f2[u] = 0;
for (int i = 0; i < len; ++i) {
f[e[u][i]] = u;
dfs2(e[u][i]);
}
e[u].clear();
}
int main() {
int x, y, op, k, z;
scanf("%d", &n);
for (int i = 1; i < n; ++i) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
L[0] = 0;
dfs(1, 0);
build(1, 1, n + 1);
scanf("%d", &m);
for (int i = 0; i < m; ++i) {
scanf("%d", &op);
if (op == 1) {
scanf("%d%d%d", &x, &y, &z);
if (dep[x] < dep[y]) swap(x, y);
long long tmp = z - b[x];
b[x] = z;
modify(1, 1, n + 1, L[x], R[x], tmp);
} else {
scanf("%d", &k);
for (int j = 0; j < k; ++j) {
scanf("%d", &a[j]);
dis[a[j]] = Query(1, 1, n + 1, L[a[j]], L[a[j]]);
c[j] = a[j];
}
get(k);
f2[rt] = 0; f[rt] = 0;
dfs1(rt);
dfs2(rt);
for (int j = 0; j < k - 1; ++j)
printf("%lld ", max(f1[c[j]], f2[c[j]]));
printf("%lld\n", max(f1[c[k - 1]], f2[c[k - 1]]));
}
}
return 0;
}
/*
Top_xiao的犯错记录:
写这个题的时候,出现了几个问题:
1. 建完虚树的时候, 把原图 和 虚树的图弄混了,竟然 用 par[u][0] 当做 u 的父亲.
2. 然后 跑 f2 的时候,不能用 f[it] 因为 f[it] 用可能还没有更新到.因为是同一个父亲节点,直接用 f[u] 就可以.
3. 第一次以 1 为根节点建虚树的时候, 1 有可能并不是关键点,但是会被算进来. 所以算 f2 的时候需要特判一下.
4. 然后换了一种建虚树的方法, 并不是 以 1 当做根节点. 这个时候根节点的 f2 还要是 0, 还需要特判一下.
但是比 上一种 特判简单.
5. 最后 我的 dis 没有更新完全, 就是只更新了关键点,那些 lca 的非关键点没有更新.tmd.
*/
树状数组版:
#include<bits/stdc++.h>
#define ls now << 1
#define rs now << 1 | 1
#define low(x) (x & (-x))
using namespace std;
const int N = 1e5 + 100;
struct edge {
int u, v, w, next;
} g[N << 1];
vector<int>e[N];
int n, m, cnt, Head[N], st, stk[N], a[N], c[N], f[N], rt;
int L[N], R[N], dep[N], par[N][20], pre[N];
long long dis[N], val[N << 2], lz[N << 2], f1[N], f2[N], b[N], ans[N];
void Add(int x, long long y){
for (; x <= n; x += low(x)) ans[x] += y;
}
long long ask(int x){
long long Ans = 0;
for (; x; x -= low(x)) Ans += ans[x];
return Ans;
}
void add(int u, int v, int w) {
++cnt;
g[cnt] = (edge) {
u, v, w, Head[u]
};
Head[u] = cnt;
}
bool cmp(const int &A, const int &B) {
return L[A] < L[B];
}
void dfs(int u, int fa) {
par[u][0] = fa;
dep[u] = dep[fa] + 1;
L[u] = ++L[0];
pre[L[0]] = u;
for (int i = 1; i < 20; ++i)
par[u][i] = par[par[u][i - 1]][i - 1];
for (int i = Head[u]; i; i = g[i].next) {
if (g[i].v == fa) continue;
b[g[i].v] = g[i].w;
dis[g[i].v] = dis[u] + 1ll * g[i].w;
dfs(g[i].v, u);
}
R[u] = L[0];
}
int LCA(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (int i = 19; i >= 0; --i)
if (dep[par[x][i]] >= dep[y]) x = par[x][i];
if (x == y) return x;
for (int i = 19; i >= 0; --i)
if (par[x][i] != par[y][i])
x = par[x][i], y = par[y][i];
return par[x][0];
}
void ins(int x) {
if (st == 0) {
stk[++st] = x;
return;
}
int t = LCA(x, stk[st]);
dis[t] = ask(L[t]);
// dis[t] = Query(1,1,n+1,L[t],L[t]);
if (t == stk[st]) {
stk[++st] = x;
return;
}
while(st > 1 && L[stk[st - 1]] >= L[t]) e[stk[st - 1]].push_back(stk[st]), st--;
if (t != stk[st]) e[t].push_back(stk[st]), stk[st] = t;
stk[++st] = x;
}
void get(int k) {
sort(a, a + k, cmp); st = 0;
for (int i = 0; i < k; ++i) ins(a[i]);
while(st > 1) e[stk[st - 1]].push_back(stk[st]), st--;
rt = stk[1];
}
void dfs1(int u) {
int len = e[u].size();
f1[u] = 0;
for (int i = 0; i < len; ++i) {
int v = e[u][i];
dfs1(v);
f1[u] = max(f1[u], f1[v] + dis[v] - dis[u]);
}
}
void dfs2(int u) {
int len = e[u].size();
int len1 = e[f[u]].size();
long long ans = 0;
for (int i = 0; i < len1; ++i) {
int it = e[f[u]][i];
if (it == u) continue;
ans = max(f1[it] + dis[it] - dis[f[u]], ans);
}
ans = max(ans, f2[f[u]]);
f2[u] = ans + dis[u] - dis[f[u]];
if (rt == u) f2[u] = 0;
for (int i = 0; i < len; ++i) {
f[e[u][i]] = u;
dfs2(e[u][i]);
}
e[u].clear();
}
int main() {
int x, y, op, k, z;
scanf("%d", &n);
for (int i = 1; i < n; ++i) {
scanf("%d%d%d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
L[0] = 0;
dfs(1, 0);
for (int i = 2; i <= n; ++i){
Add(L[i],b[i]); Add(R[i]+1, -b[i]);
}
scanf("%d", &m);
for (int i = 0; i < m; ++i) {
scanf("%d", &op);
if (op == 1) {
scanf("%d%d%d", &x, &y, &z);
if (dep[x] < dep[y]) swap(x, y);
long long tmp = z - b[x];
b[x] = z;
Add(L[x],tmp); Add(R[x]+1,-tmp);
} else {
scanf("%d", &k);
for (int j = 0; j < k; ++j) {
scanf("%d", &a[j]);
dis[a[j]] = ask(L[a[j]]);
c[j] = a[j];
}
get(k);
f2[rt] = 0; f[rt] = 0;
dfs1(rt);
dfs2(rt);
for (int j = 0; j < k - 1; ++j)
printf("%lld ", max(f1[c[j]], f2[c[j]]));
printf("%lld\n", max(f1[c[k - 1]], f2[c[k - 1]]));
}
}
return 0;
}