The 13th Chinese Northeast Collegiate Programming Contest D. Master of Data Structure 虚树

D. Master of Data Structure

题意:给你一颗树,然后有m次操作,每次操作都会选择一条链进行操作,具体操作如题。
思路:我们发现m只有2000次,因此每次对一条链操作,该链至多只有2000个权值不同的节点,因此我们可以把权值相同的节点缩为一个点,这样至多只要更新2000个节点就行,那么我们离线对m次操作选取的节点建立一颗虚树,虚树的边维护两个信息,一个是两点之间有多少个节点,一个是两点之间的点权值,然后就暴力更新查询就搞定了
#include<bits/stdc++.h>
#define pi pair<int, long long>
#define mk make_pair
#define pb push_back
#define ll long long
using namespace std;
const int maxn = 5e5 + 10;
vector<int> G[maxn];
pi W[maxn];
int dep[maxn], f[maxn], vis[maxn], a[maxn];
int anc[maxn][20], id[maxn], S[6100], cnt, cnt2, Top;
ll val[maxn];
struct node {
    int opt, u, v, k;
} q[2010];
void dfs(int u, int fa, int rt) {
    dep[u] = dep[fa] + 1;
    anc[u][0] = fa;
    for (int i = 1; i < 20; i++)
        anc[u][i] = anc[anc[u][i - 1]][i - 1];
    id[u] = ++cnt;
    if (vis[u])
        a[++cnt2] = u;
    for (auto v : G[u])
        if (v != fa)
            dfs(v, u, rt);
}
int LCA(int u, int v) {
    if (dep[u] < dep[v])
        swap(u, v);
    for (int i = 19; i >= 0; i--)
        if (dep[anc[u][i]] >= dep[v])
            u = anc[u][i];
    if (u == v)
        return u;
    for (int i = 19; i >=0; i--)
        if (anc[u][i] != anc[v][i])
            u = anc[u][i], v = anc[v][i];
    return anc[u][0];
}
void work(int x, int opt, int k, int tp, ll &res, ll &mx, ll &mn) {
    if (opt == 1) {
        val[x] += k;
        if (tp)
            W[x].second += k;
    }
    else if (opt == 2) {
        val[x] ^= k;
        if (tp)
            W[x].second ^= k;
    }
    else if (opt == 3) {
        if (val[x] >= k)
            val[x] -= k;
        if (tp && W[x].second >= k)
            W[x].second -= k;
    }
    else if (opt == 4) {
        res += val[x];
        if (tp)
            res += 1ll * W[x].first * W[x].second;
    }
    else if (opt == 5) {
        res ^= val[x];
        if (tp && W[x].first % 2)
            res ^= W[x].second;
    }
    else if (opt == 6) {
        mx = max(mx, val[x]);
        mn = min(mn, val[x]);
        if (tp && W[x].first) {
            mx = max(mx, W[x].second);
            mn = min(mn, W[x].second);
        }
        res = mx - mn;
    }
    else {
        mn = min(mn, abs(val[x] - 1ll * k));
        if (tp && W[x].first)
            mn = min(mn, abs(W[x].second - 1ll * k));
        res = mn;
    }
}
void gao(int opt, int x, int y, int k) {
    ll res = 0, mx = 0, mn = 1e18;
    while (x != y) {
        if (dep[x] < dep[y])
            swap(x, y);
        work(x, opt, k, 1, res, mx, mn);
        x = f[x];
    }
    work(x, opt, k, 0, res, mx, mn);
    if (opt >= 4)
        printf("%lld\n", res);
}
void add(int u, int v) {
    if (dep[u] < dep[v])
        swap(u, v);
    f[u] = v;
    val[u] = 0;
    W[u] = mk(dep[u] - dep[v] - 1, 0);
}
void insert(int x) {
    if (Top == 1) {
        S[++Top] = x;
        return;
    }
    int lca = LCA(x, S[Top]);
    if (lca == S[Top]) {
        S[++Top] = x;
        return;
    }
    while (Top > 1 && id[S[Top - 1]] >= id[lca])
        add(S[Top - 1], S[Top]), Top--;
    if (lca != S[Top])
        add(lca, S[Top]), S[Top] = lca;
    S[++Top] = x;
}
void build() {
    S[Top = 1] = 0;
    for (int i = 1; i <= cnt2; i++)
        insert(a[i]);
    while (Top > 1)
        add(S[Top - 1], S[Top]), Top--;
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, m, u, v;
        cnt = cnt2 = 0;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            G[i].clear(), vis[i] = 0;
        for (int i = 1; i < n; i++) {
            scanf("%d%d", &u, &v);
            G[u].pb(v);
            G[v].pb(u);
        }
        for (int i = 1; i <= m; i++) {
            scanf("%d%d%d", &q[i].opt, &q[i].u, &q[i].v);
            if (q[i].opt <= 3 || q[i].opt >= 7)
                scanf("%d", &q[i].k);
            vis[q[i].u] = vis[q[i].v] = 1;
        }
        vis[1] = 1;
        dfs(1, 0, 0);
        build();
        for (int i = 1; i <= m; i++)
            gao(q[i].opt, q[i].u, q[i].v, q[i].k);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值