bzoj3052糖果公园 树上莫队

题目传送门: http://www.lydsy.com/JudgeOnline/problem.php?id=3052

最近不太想写博客了,退役前还是写点学的东西吧。
这个坑从我会莫队开始就挖在这里了。。
莫队: http://blog.csdn.net/nickwzk/article/details/52954097
可是在树上怎么莫队呢?
这有两种非常妙的方法,dfs序和王室联邦分块(bzoj1086)
我看网上大家都用dfs序的版本,就作死写了一个王室联邦分块的,然后常数巨大(大概不怪王室联邦分块怪我常数大
然后这个是一个树上的带修改的莫队,所以当块大小等于 n23 的时候复杂度最优。
借用王室联邦分块,另块大小为B= n23 ,块的个数为p。每个节点i属于belong[i]。我们可以发现(belong[u], belong[v])最多 p2 个取值。
因为已经按块排好序了,所以我们可以将情况分为两种。一种是当前移动后u,v都在原块内,那么移动总复杂度为O( Bq ;另一种是u,v不在原来的块内,那么这种情况最多出现 p2 ,每次移动最坏情况为O( n+n+q )。又因为n与q同阶,所以可以得到总的时间复杂度为O( n53
然后在树上移动的时候,记录一个vis数组,到达一个点的时候将其取反,如果已经存在当前统计中就将它删去,若没有就将它加入。对于时间的修改可以放在移动之前也可以放在移动之后,必须要使这个操作可逆,所以修改之后要记录下修改之前的糖果种类。
对于lca(u,v),要在记录答案之前加上,在记录答案之后删去
自带巨大常数的Code:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

const int MAXN = 100003;
const int S = 17;
struct Edge {
    int to, nxt;
    Edge() {}
    Edge(int _to, int _nxt):to(_to), nxt(_nxt) {}
}e[MAXN << 1];
int h[MAXN], p;
int stack[MAXN], top, belong[MAXN], cnt, sz;
struct Node {
    int l, r, id, ti;
    bool operator < (const Node &x) const {
        return belong[l] < belong[x.l] || (belong[l] == belong[x.l] && belong[r] < belong[x.r]) || (belong[l] == belong[x.l] && belong[r] == belong[x.r] && ti < x.ti);
    }
}q[MAXN];
struct Node2 {
    int l, r, ti;
}qq[MAXN];
int n, m, Q, Q0, Q1;
int V[MAXN], W[MAXN], C[MAXN];
int fa[MAXN][S + 3], dep[MAXN];
long long ans[MAXN], tans;
int vis[MAXN], cur[MAXN];
long long sum[MAXN];
int l, r, tm;

inline int read() {
    int x = 0; char ch = getchar(); bool fg = 0;
    while(ch < '0' || ch > '9') { if(ch == '-') fg = 1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return fg ? -x : x;
}

inline void add_edge(int u, int v) {
    e[++p] = Edge(v, h[u]); h[u] = p;
    e[++p] = Edge(u, h[v]); h[v] = p;
}

void dfs(int u, int f) {
    fa[u][0] = f;
    dep[u] = dep[f] + 1;
    int bot = top;
    for(int i = h[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if(v == f) continue;
        dfs(v, u);
        if(top - bot >= sz) {
            cnt++;
            while(top != bot) belong[stack[top--]] = cnt;
        }
    }
    stack[++top] = u;
}

void G(int &u, int step) {
    for(int i = 0; i < S; i++) if((1 << i) & step) u = fa[u][i];
}

int lca(int u, int v) {
    if(dep[u] > dep[v]) swap(u, v);
    G(v, dep[v] - dep[u]);
    if(u == v) return u;
    for(int i = S; i >= 0; i--) if(fa[u][i] != fa[v][i]) {
        u = fa[u][i]; v = fa[v][i];
    }
    return fa[u][0];
}

inline void modify(int u) {
    tans -= V[C[u]] * sum[cur[C[u]]];
    cur[C[u]] += vis[u];
    vis[u] = -vis[u];
    tans += V[C[u]] * sum[cur[C[u]]];
}

inline void update(int u, int v) {
    if(u == v) return;
    if(dep[u] > dep[v]) swap(u, v);
    while(dep[v] > dep[u]) {
        modify(v);
        v = fa[v][0];
    }
    while(u != v) {
        modify(u); modify(v);
        u = fa[u][0]; v = fa[v][0];
    }
}

inline void upd(int t) {
    if(vis[qq[t].l] == -1) {
        modify(qq[t].l);
        swap(C[qq[t].l], qq[t].r);
        modify(qq[t].l);
    }
    else swap(C[qq[t].l], qq[t].r);
}

inline void moveto(int u, int v) {
    update(l, u); update(r, v);
    l = u; r = v;
}

int main() {
    n = read(); m = read(); Q = read();
    sz = (int)pow(n, 2.0 / 3.0);
    for(int i = 1; i <= m; i++) V[i] = read();
    for(int i = 1; i <= n; i++) W[i] = read();
    for(int i = 1, u, v; i < n; i++) {
        u = read(); v = read();
        add_edge(u, v);
    }
    for(int i = 1; i <= n; i++) {
        C[i] = read();
        vis[i] = 1;
        sum[i] = sum[i - 1] + W[i];
    }
    for(int i = 1, tp; i <= Q; i++) {
        tp = read(); 
        if(tp) {
            ++Q1;
            q[Q1].l = read(); q[Q1].r = read();
            q[Q1].id = Q1;
            q[Q1].ti = i;
        }
        else {
            ++Q0;
            qq[Q0].l = read(); qq[Q0].r = read();
            qq[Q0].ti = i;
        }
    }
    dfs(1, 0);
    while(top) belong[stack[top--]] = cnt;
    sort(q + 1, q + Q1 + 1);
    for(int k = 1; k <= S; k++) {
        for(int i = 1; i <= n; i++) {
            fa[i][k] = fa[fa[i][k - 1]][k - 1];
        }
    }
    for(int i = 1; i <= Q1; i++) {
        if(belong[q[i].l] > belong[q[i].r]) swap(q[i].l, q[i].r);
        moveto(q[i].l, q[i].r);
        int lc = lca(l, r);
        modify(lc);
        while(qq[tm + 1].ti < q[i].ti && tm < Q0) upd(++tm);
        while(qq[tm].ti > q[i].ti) upd(tm--);
        ans[q[i].id] = tans;
        modify(lc);
    }
    for(int i = 1; i <= Q1; 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、付费专栏及课程。

余额充值