【WC2013】糖果公园 树上莫队

树上莫队,将树分块,以x,y为一二关键字,以时间为第三关键字。暴力修改。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
const int N = 100005, B = 2500, D = 18;
struct arr { int x, y, n, z; } q[N], p[N];
struct Edge { int y, nex; } g[N * 2];
int n, col[N], W[N], cl, ti[N], top, st[N], Up[N][D], par[N], lp, lq, c[N], c1[N], h[N];
int pos[N], sz, u[N], m, Q;
LL sum, ans[N], V[N]; bool vis[N], Ty[N];
void Init(int x, int y) { g[++ sz] = (Edge) { y, pos[x] }, pos[x] = sz; }
void C(int x) {
    int c1 = c[x];
    if (vis[x]) sum -= V[c1] * W[ ti[c1] ], ti[c1] --;
    else ti[c1] ++, sum += V[c1] * W[ ti[c1] ];
    vis[x] ^= 1;
}
int Lca(int x, int y) {
    if (h[x] < h[y]) swap(x, y);
    Dwn(i, D - 1, 0) if ((1 << i) <= h[x] - h[y]) x = Up[x][i];
    if (x == y) return x;
    Dwn(i, D - 1, 0) if (Up[x][i] != Up[y][i]) x = Up[x][i], y = Up[y][i];
    return Up[x][0];
}
void Dfs(int x) {
    Up[x][0] = par[x]; u[x] = top;
    Rep(i, 1, D - 1) Up[x][i] = Up[ Up[x][i - 1] ][i - 1];
    RepE(i, x) {
        int y = g[i].y; if (y == par[x]) continue ;
        par[y] = x, h[y] = h[x] + 1; Dfs(y);
        if (top - u[x] > B) {
            cl ++; while (top > u[x]) col[ st[top --] ] = cl;
        }
    }
    st[++ top] = x;
}
void Change(int x, int y) { if (vis[x]) C(x), c[x] = y, C(x); else c[x] = y; }
void Modify(int x, int y) {
    if (h[x] < h[y]) swap(x, y);
    while (h[x] > h[y]) C(x), x = par[x];
    while (x != y) C(x), C(y), x = par[x], y = par[y];
}
bool cmp(arr a, arr b) {
    if (col[ a.x ] != col[ b.x ]) return col[ a.x ] < col[ b.x ];
    return (col[ a.y ] == col[ b.y ]) ? a.n < b.n : col[ a.y ] < col[ b.y ];
}
int main()
{
    scanf ("%d%d%d", &n, &m, &Q);
    Rep(i, 1, m) scanf ("%lld", &V[i]); // val
    Rep(i, 1, n) scanf ("%d", &W[i]); // time i
    Rep(i, 1, n - 1) {
        int x, y; scanf ("%d%d", &x, &y);
        Init(x, y), Init(y, x);
    }
    Rep(i, 1, n) scanf ("%d", &c[i]), c1[i] = c[i];
    Dfs(1); Rep(i, 1, top) col[ st[i] ] = cl;
    Rep(i, 1, Q) {
        int x, y; scanf ("%d%d%d", &Ty[i], &x, &y);
        if (Ty[i]) {
            if (col[x] > col[y]) swap(x, y);
            q[++ lq] = (arr) { x, y, i, 0 };
        } else {
            p[++ lp] = (arr) { x, y, i, c1[x] }, c1[x] = y;
        }
    }
    sort(q + 1, q + lq + 1, cmp);
    int t = 1, qu = 1, qv = 1;
    Rep(i, 1, lq) {
        int u = q[i].x, v = q[i].y, z = Lca(u, v);
        while (t <= lp && p[t].n < q[i].n) Change(p[t].x, p[t].y), t ++;
        while (t > 1 && p[t - 1].n > q[i].n) t --, Change(p[t].x, p[t].z);
        Modify(qu, u), Modify(qv, v);
        C(z), ans[ q[i].n ] = sum, C(z);
        qu = u, qv = v;
    }
    Rep(i, 1, Q) if (Ty[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、付费专栏及课程。

余额充值