UOJ58 糖果公园(树上带修改莫队)

题目链接

思路:
             网上题解都很多。。。 学习树上带修改莫队的蒟蒻一只。
             大概想法就是, 跑出 dfs d f s 序,这个 dfs d f s 序进入每个点 x x 的时候记录一下当前位置lx,每个点出来的时候也记录一下位置 rx r x ,然后对这个 dfs d f s 序进行分块, 就和普通序列分块一样了。
             树上普通莫队查询 x,y x , y 之间的信息的话,如果 x,y x , y 属于同一条链,假设 x x y前面出现,那么对应的查询区间就是 [lx,ly] [ l x , l y ] ,不用担心在 x x y之间有其他子树存在,因为如果有, 那么也一定会对称遍历完, 我们可以用一个 vis v i s 数组记录每个点访问的次数,访问了偶数次(可用异或操作)的话相当于没有访问,那么最终就只有 x x y的路径上的点存在了。对于 x,y x , y 在不同链上, 那么询问区间就取 [rx,ly] [ r x , l y ] ,同样如果 x,y x , y 之间的子树也会对称遍历完,最终只剩下 x x y的简单路径, 注意这个时候 LCA L C A 的信息没有加入进去,单独处理下 LCA L C A 即可。
             带修改也就是记录下第三个信息,模拟操作执行和撤销操作, 这部分暴力即可。

#include<bits/stdc++.h>
typedef long long ll;
const ll maxn = 2e5 + 10;
const ll block_size = 1610;
using namespace std;

struct opr {
    ll id, u, v, tim, lca;
    opr() {}
    opr(ll id, ll u, ll v, ll tim, ll lca) :
        id(id), u(u), v(v), tim(tim), lca(lca) {}
    bool operator < (opr p) const {
        ll bu = u / block_size, pu = p.u / block_size;
        ll bv = v / block_size, pv = p.v / block_size;
        if(bu != pu) return bu < pu;
        if(bv != pv) return bv < pv;
        return tim < p.tim;
    }
} rec[maxn];
ll n, m, q, vis[maxn];
vector<ll> G[maxn];
ll anc[maxn][18], deep[maxn], tot[maxn];
ll ans[maxn], v[maxn], w[maxn], sum[maxn], now_ans;
ll l[maxn], r[maxn], dfn[maxn], num, c[maxn];
ll id[maxn], last[maxn], now[maxn], res[maxn];

void dfs(ll x, ll fa, ll d) {
    l[x] = num; dfn[num++] = x;
    anc[x][0] = fa; deep[x] = d;
    for(ll i = 1; i < 18; i++) {
        ll t = anc[x][i - 1];
        if(~t) anc[x][i] = anc[t][i - 1];
    }
    for(ll i = 0; i < G[x].size(); i++) {
        ll v = G[x][i];
        if(v == fa) continue;
        dfs(v, x, d + 1);
    }
    dfn[num++] = x;
    r[x] = num - 1;
}

ll lca(ll x, ll y) {
    if(deep[x] < deep[y]) swap(x, y);
    for(ll lg = 17; lg >= 0; lg--) {
        if(deep[x] - (1 << lg) < deep[y]) continue;
        x = anc[x][lg];
        if(deep[x] == deep[y]) break;
    }
    if(x == y) return x;
    for(ll lg = 17; lg >= 0; lg--) {
        if(anc[x][lg] == anc[y][lg]) continue;
        x = anc[x][lg]; y = anc[y][lg];
    }
    return anc[x][0];
}

void solve_poll(ll x) {
    vis[x] ^= 1;
    ll co = c[x];
    if(vis[x]) { tot[c[x]]++; now_ans += (sum[tot[co]] - sum[tot[co] - 1]) * v[co]; }
    else { tot[co]--; now_ans += (sum[tot[co]] - sum[tot[co] + 1]) * v[co]; }
}

void solve(ll now_id, ll las_l, ll las_r, ll las_t, ll now_l, ll now_r, ll now_t) {
    while(las_t < now_t) {
        las_t++;
        ll u = id[las_t], od = last[las_t], nw = now[las_t];
        if(vis[u]) {
            now_ans += (sum[tot[od] - 1] - sum[tot[od]]) * v[od];
            now_ans += (sum[tot[nw] + 1] - sum[tot[nw]]) * v[nw];
            tot[od]--; tot[nw]++;
        }
        c[u] = nw;
    }
    while(las_t > now_t) {
        ll u = id[las_t], od = now[las_t], nw = last[las_t];
        if(vis[u]) {
            now_ans += (sum[tot[od] - 1] - sum[tot[od]]) * v[od];
            now_ans += (sum[tot[nw] + 1] - sum[tot[nw]]) * v[nw];
            tot[od]--; tot[nw]++;
        }
        c[u] = nw; las_t--;
    }

    while(las_l < now_l) { solve_poll(dfn[las_l]); las_l++; }
    while(las_r > now_r) { solve_poll(dfn[las_r]); las_r--; }
    while(las_l > now_l) { las_l--; solve_poll(dfn[las_l]); }
    while(las_r < now_r) { las_r++; solve_poll(dfn[las_r]); }
    ans[now_id] = now_ans;
}

const ll MAX = 10000;
char buf[MAX], *ps = buf, *pe = buf + 1;
inline void rnext() {
    if(++ps == pe) pe = (ps = buf) + fread(buf, sizeof(char), sizeof(buf) / sizeof(char), stdin);
}
template <class T>
inline bool in(T &ans) {
    ans = 0;
    T f = 1;
    if(ps == pe) return false;//EOF
    do{
        rnext();
        if('-' == *ps) f = -1;
    } while(!isdigit(*ps) && ps != pe);
    if(ps == pe) return false;//EOF
    do {
        ans = (ans<<1)+(ans<<3)+*ps-48;
        rnext();
    } while(isdigit(*ps) && ps != pe);
    ans *= f;
    return true;
}

int main() {
    while(in(n)) {
        in(m); in(q);
        ll number = 0; num = 1;
        now_ans = 0;
        memset(vis, 0, sizeof vis);
        for(ll i = 0; i < maxn; i++) {
            G[i].clear();
            ans[i] = -1;
            tot[i] = vis[i] = 0;
            for(ll j = 0; j < 18; j++) anc[i][j] = -1;
        }
        for(ll i = 1; i <= m; i++) in(v[i]);
        sum[0] = 0;
        for(ll i = 1; i <= n; i++) { in(w[i]); sum[i] = sum[i - 1] + w[i]; }
        for(ll i = 1; i < n; i++) {
            ll u, v; in(u); in(v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        for(ll i = 1; i <= n; i++) { in(c[i]); res[i] = c[i]; }
        dfs(1, -1, 1);
        ll now_time = 0;
        for(ll i = 0; i < q; i++) {
            ll op, u, v;
            in(op); in(u); in(v);
            if(op == 1) {
                if(l[u] > l[v]) swap(u, v);
                ll LCA = lca(u, v);
                if(LCA == u || LCA == v) rec[number++] = opr(i, l[u], l[v], now_time, LCA);
                else rec[number++] = opr(i, r[u], l[v], now_time, LCA);
            } else {
                now_time++; id[now_time] = u;
                last[now_time] = c[u]; now[now_time] = v; c[u] = v;
            }
        }
        sort(rec, rec + number);
        now_ans = 0;
        for(ll i = 1; i <= n; i++) c[i] = res[i];
        ll las_l = 0, las_r = 0, las_t = 0;
        for(ll i = 0; i < number; i++) {
            ll now_l = rec[i].u, now_r = rec[i].v, LCA = rec[i].lca, idx = rec[i].id, n_t = rec[i].tim;
            solve(rec[i].id, las_l, las_r, las_t, now_l, now_r, n_t);
            if(LCA != dfn[now_l] && LCA != dfn[now_r]) {
                ll col = c[LCA];
                ans[rec[i].id] += (sum[tot[col] + 1] - sum[tot[col]]) * v[col];
            }
            las_l = now_l; las_r = now_r; las_t = n_t;
        }
        for(ll i = 0; i < q; i++) if(~ans[i]) printf("%lld\n", ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值