Jittery Roads Gym - 100889J (虚树 + DP + dfs 序, + 线段树)

每次给一个点集, 求每个点到其他所有点的最大距离:

会修改边权;   

修改边权之后,

我们可以用 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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值