bzoj 3052. [wc2013]糖果公园 树上带修莫队

题目

bzoj3052 [wc2013]糖果公园 (https://darkbzoj.tk/problem/3052)

题目大意

有一座糖果公园,有n个游览点构成(编号1~n),每个游览点都有一个糖果发放处。有n-1条双向道路,形成一个树结构。

糖果公园所发放的糖果数量非常丰富,总共有m种,题目的编号为1~m。每个发放点只发放一个特定的糖果。

来到公园的人,总是从一个游览点出发点通过最短路径到达另一游览点。他们每经过一个游览点就可以得到相应一个糖果。

大家对不同类型的糖果的喜欢程度不同,如果游客第i次品尝第j种糖果,那么他的愉悦指数H+=V[j]W[i]

当然公园糖果发放点的发放糖果的种类不一定是一成不变的。有时,一些糖果点发放的糖果种类可能会改变(也会是m种中的一种)
这样的目的是能够让游客们感到惊喜。

现在你的任务是统计每个游客的愉悦指数。

输入

n, m ,q
v[1], v[2], ... , v[m]
w[1], w[2], ... , w[n]

A[i] B[i](A[i]和B[i]有边)
....
A[n-1] B[n-1]

c[1], c[2], ... , c[n] (每个游览点的发放糖果种类)

接下来q行 Type x y
Type=0, 把x游览点的糖果种类换成y
Type=1,查询从x出发到y的愉悦指数

输出

对于每次查询输出H

数据范围

1<=n, m, q<=1e5, time=100s

思路

树上带修莫队的模板题。时间复杂度N^(5/3)
ps:树链剖分LCA更快

//18324ms
#include<bits/stdc++.h>
#define LL long long
using namespace std;

struct edge {
    int to, nxt;
} e[200005<<1];

int head[200005], lg[200005], cut=0;

struct Tree_lca {

    int f[200005][22], deep[200005], in[200005], out[200005], T=0;
    int id[200005];
    void init() {
        memset(head, -1, sizeof(head));
        cut=T=0;
    }
    void addedge(int x, int y) {
        e[++cut]= {y, head[x]};
        head[x]=cut;
    }
    void dfs(int u, int fa) {
        f[u][0]=fa;
        in[u]=++T;
        id[T]=u;
        deep[u]=deep[fa]+1;
        for(int i=1; i<=lg[deep[u]]; i++) {
            f[u][i]=f[f[u][i-1]][i-1];
        }
        for(int i=head[u]; i!=-1; i=e[i].nxt) {
            int to=e[i].to;
            if(to!=fa) {
                dfs(to, u);
            }
        }
        out[u]=++T;
        id[T]=u;
    }
    int LCA(int x, int y) {
        if(deep[x]<deep[y])
            swap(x, y);
        int d=deep[x]-deep[y];
        for(int i=0; d; i++) {
            if(d&(1<<i)) {
                x=f[x][i];
                d-=(1<<i);
            }
        }
        if(x==y)
            return x;
        for(int i=lg[deep[y]]; i>=0; i--) {
            if(f[x][i]!=f[y][i]) {
                x=f[x][i], y=f[y][i];
            }
        }
        return f[x][0];
    }

} Tree;

int vis[200005], c[200005];
LL v[200005], w[200005];
LL sum[200005], ans[200005];
LL ANS=0;

void add(int x) {
    sum[x]++;
    ANS+=(w[sum[x]]*v[x]);
}

void del(int x) {
    ANS-=(w[sum[x]]*v[x]);
    sum[x]--;
}

void calc(int x) {
    x=Tree.id[x];
    if(!vis[x]) {
        add(c[x]);
    } else {
        del(c[x]);
    }
    vis[x]^=1;
}

struct Qry {
    int op, l, r, t, k, lca, i;
} q[200005], qx[200005];

void up(int i, int t) {//时间轴的移动

    if(vis[qx[t].l]) {//如果修改的点在路径上,那么修改造成贡献

        del(c[qx[t].l]);
        add(qx[t].r);

    }
    swap(c[qx[t].l], qx[t].r);
}

int len;
void getans(int Q) {
    sort(q+1, q+Q+1, [](const Qry &a, const Qry &b){
        if (a.l/len!=b.l/len) return a.l<b.l;
		if (a.r/len!=b.r/len) return a.r<b.r;
		return a.t<b.t;
    });
    int L=1, R=0, Ti=0;
    for(int i=1; i<=Q; i++) {
        while(L<q[i].l) {
            calc(L);
            L++;
        }
        while(L>q[i].l) {
            L--;
            calc(L);
        }
        while(R<q[i].r) {
            R++;
            calc(R);
        }
        while(R>q[i].r) {
            calc(R);
            R--;
        }
        if(q[i].lca)
            calc(q[i].lca);

        //时间轴的影响
        while(Ti<q[i].t) {
            up(i, ++Ti);
        }
        while(Ti>q[i].t) {
            up(i, Ti--);
        }

        ans[q[i].i]=ANS;

        if(q[i].lca)
            calc(q[i].lca);
    }
}

int main() {

    lg[1]=1;
    for(int i=2; i<=200005; i++) {
        lg[i]=lg[i>>1]+1;
    }
    int n, m, Q;
    scanf("%d%d%d", &n, &m, &Q);
    Tree.init();

    len=sqrt(2*n);

    for(int i=1; i<=m; i++) {
        scanf("%lld", &v[i]);
    }
    for(int i=1; i<=n; i++) {
        scanf("%lld", &w[i]);
    }
    for(int i=1; i<=n-1; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        Tree.addedge(x, y);
        Tree.addedge(y, x);
    }
    for(int i=1; i<=n; i++) {
        scanf("%d", &c[i]);
    }
    Tree.dfs(1, 0);

    int cut_x=0, cut_q=0;
    for(int i=1; i<=Q; i++) {
        int op, x, y;
        scanf("%d%d%d",&op, &x, &y);
        int lca=Tree.LCA(x, y);
        if(op==1) {//查询

            if(Tree.in[x]>Tree.in[y]) {
                swap(x, y);
            }
            ++cut_q;
            q[cut_q].l=(lca==x)?Tree.in[x]:Tree.out[x];
            q[cut_q].r=Tree.in[y];
            q[cut_q].t=cut_x;//记录查询是第cut_x次修改后面

            q[cut_q].k=q[i].l/len;
            q[cut_q].lca=(lca==x)?0:Tree.in[lca];
            q[cut_q].i=cut_q;

        } else {//修改
            ++cut_x;
            qx[cut_x].l=x;
            qx[cut_x].r=y;
            qx[cut_x].lca=(lca==x)?0:Tree.in[lca];
        }
    }
    getans(cut_q);
    for(int i=1; i<=cut_q; i++) {
        printf("%lld\n", ans[i]);
    }

    return 0;
}
/*
4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2

84
131
27
84
*/
posted @   liweihang  阅读(135)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
Live2D
欢迎阅读『bzoj 3052. [wc2013]糖果公园 树上带修莫队』
点击右上角即可分享
微信分享提示