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

本文详细介绍了如何使用树状数组和LCA(最近公共祖先)算法来高效解决区间加权和的动态维护问题。通过C++实现了一个完整的程序,包括输入处理、边的添加、深度优先搜索、最近公共祖先查找、区间更新和查询等关键步骤。程序处理了大量输入数据,并确保了在复杂场景下的正确性和效率。
摘要由CSDN通过智能技术生成

写了一下午太不容易了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cctype>
#define Debug(x) std::cerr << #x << "=" << x << std::endl
typedef long long ll;
const int N = 2e5 + 5;
const int M = 1e6 + 5;
void read(int &x){
    x = 0;int f = 1;char c = getchar();
    for(;!isdigit(c);c = getchar())if(c == '-')f = -1;
    for(;isdigit(c);c = getchar())x = (x << 1) + (x << 3) + c - '0';
    x *= f;
}
int n,m,Q,block;
int v[N],w[N],seq[N << 1],fa[N][20],cnt[M],top,first[N],last[N],deep[N],c[N],st[N];
ll ans[N];
struct Edge{
    int node;
    int next;
    Edge(int node = 0,int next = 0):node(node),next(next){}
}edge[N << 1];
int head[N],tot = 1;
void add_edge(int u,int v){
    edge[++tot] = Edge(v,head[u]);
    head[u] = tot;
}
struct Query{
    int id,l,r,p,t;
    Query(int id = 0,int l = 0,int r = 0,int p = 0,int t = 0):id(id),l(l),r(r),p(p),t(t){}
    bool operator < (const Query& P){
        return (l / block) ^ (P.l / block) ? l < P.l : (r / block) ^ (P.r / block) ? r < P.r : t < P.t;
    }
}q[N];
struct Update{
    int id,x,y;
}upd[N];
void euper(int u,int p){
    seq[++top] = u;
    first[u] = top;
    for(int i = head[u];i;i = edge[i].next){
        int v = edge[i].node;
        if(v != p)euper(v,u);
    }
    seq[++top] = u;
    last[u] = top;
}
void dfs(int u,int p){
    deep[u] = deep[p] + 1;
    fa[u][0] = p;
    for(int i = 1;deep[u] >= (1 << i);i++){
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    }
    for(int i = head[u];i;i = edge[i].next){
        int v = edge[i].node;
        if(!deep[v])dfs(v,u);
    }
}
inline int lca(int u,int v){
    if(deep[u] < deep[v])std::swap(u,v);
    for(int i = 18;i >= 0;i--){
        if(deep[fa[u][i]] >= deep[v]){
            u = fa[u][i];
        }
    }
    if(u == v)return u;
    for(int i = 18;i >= 0;i--){
        if(fa[u][i] == fa[v][i])continue;
        u = fa[u][i];
        v = fa[v][i];
    }
    return fa[u][0];
}
inline ll add(int x){
    st[x] ^= 1;
    if(st[x]){
        ++cnt[c[x]];
        return (ll)v[c[x]] * (ll)w[cnt[c[x]]]; 
    }
    else {
        ll res = (ll)v[c[x]] * (ll)w[cnt[c[x]]];
        --cnt[c[x]];
        return -res;
    }
}
inline ll amend(int t,int x){
    ll res = 0;
    if(st[upd[t].x] == 0){
        std::swap(upd[t].y,c[upd[t].x]);
        return res;
    }
    // if(q[x].l <= first[upd[t].x] && q[x].r >= last[upd[t].x] ){
    //     std::swap(upd[t].y,c[upd[t].x]);
    //     return res;
    // }
    // else if(q[x].l > first[upd[t].x] && q[x].r < last[upd[t].x]){
    //     std::swap(upd[t].y,c[upd[t].x]);
    //     return res;
    // }
    else {
        ++cnt[upd[t].y];
        res += (ll)v[upd[t].y] * w[cnt[upd[t].y]];
        res -= (ll)v[c[upd[t].x]] * w[cnt[c[upd[t].x]]];
        --cnt[c[upd[t].x]];
        std::swap(upd[t].y,c[upd[t].x]);
        return res;
    }
}
int main(int argc,char *argv[]){
    read(n);read(m);read(Q);
    for(int i = 1;i <= m;i++){
        read(v[i]);
    }
    for(int i = 1;i <= n;i++){
        read(w[i]);
    }
    for(int i = 1;i < n;i++){
        int u,v;
        read(u);
        read(v);
        add_edge(u,v);
        add_edge(v,u);
    }
    for(int i = 1;i <= n;i++){
        read(c[i]);
    }
    euper(1,0);
    dfs(1,0);
    int qcnt = 0,ucnt = 0;
    for(int i = 1;i <= Q;i++){
        int type,x,y;
        read(type);
        read(x);
        read(y);
        if(type){
            if(first[x] > first[y])std::swap(x,y);
            int p = lca(x,y);
            if(x == p)++qcnt,q[qcnt] = Query(qcnt,first[x],first[y],0,ucnt);
            else ++qcnt,q[qcnt] = Query(qcnt,last[x],first[y],p,ucnt);
        }
        else {
            upd[++ucnt].id = ucnt;
            upd[ucnt].x = x;
            upd[ucnt].y = y;
        }
    }
    block = pow((double)top,0.666);
    std::sort(q + 1,q + qcnt + 1);
    ll res = 0;
    int ql = 1,qr = 0,qt = 0;
    // for(int i = 1;i <= qcnt;i++){
    //     printf("id = %d l = %d r = %d p = %d t = %d\n",q[i].id,q[i].l,q[i].r,q[i].p,q[i].t);
    // }
    for(int i = 1;i <= qcnt;i++){
        int id = q[i].id,l = q[i].l,r = q[i].r,t = q[i].t;
        while(ql < l)res += add(seq[ql++]);
        while(ql > l)res += add(seq[--ql]);
        while(qr > r)res += add(seq[qr--]);
        while(qr < r)res += add(seq[++qr]);
        if(q[i].p)res += add(q[i].p);
        while(t < qt)res += amend(qt--,i);
        while(t > qt)res += amend(++qt,i);
        ans[id] = res;
        if(q[i].p)res += add(q[i].p);
    }
    for(int i = 1;i <= qcnt;i++)printf("%lld\n",ans[i]);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值