[WC2013]糖果公园

题目

总算知道欧拉序和树上莫队是个什么东西了

看到这个计算贡献的方式不禁想到了莫队

带修莫队非常\(naive\)就是多加一维时间

来讲讲树上莫队

如果我们需要子树莫队的话,我们可以直接用\(dfs\)序把子树转化成一段区间

但是树上的一条路径显然不是很方便这样做了

于是我们考虑使用欧拉序

欧拉序就是在\(dfs\)进入一个点的时候给它一个编号,记做\(st_x\),离开这个点的时候又给它一个编号,记做\(ed_x\)

这样就得到了一个长度为\(2n\)的括号序列

对于一条路径\((x,y)\),我们强行使得\(st_x<st_y\)

如果满足\(lca(x,y)=x\),那么对应的就是区间就是\([st_x,st_y]\),但是我们只计算这里面出现了一次的点,出现了两次或\(0\)次的点不在这条路径上

如果没有满足\(x\)\(y\)的祖先,对应的区间就是\([ed_x,st_y]\),这样就会发现\(lca\)\(x\)路径上的点只有\(ed\)出现在了这个区间里,\(lca\)\(y\)的路径上的点只有\(st\)出现在这个区间里,但是我们还需要特判一下\(lca\)

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define re register
#define LL long long
const int maxn=100005;
inline int read() {
    char c=getchar();int x=0;while(c<'0'||x>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
struct Ask{int l,r,o,rk,t;}q[maxn];
int head[maxn],son[maxn],sum[maxn],deep[maxn],col[maxn];
int ed[maxn],st[maxn],dfn[maxn<<1],top[maxn],fa[maxn],w[maxn];
int pos[maxn],val[maxn],id[maxn<<1],tmp[maxn],tax[maxn],a[maxn];
int n,m,num,sz,tot,cnt,__,Q;
LL ans,Ans[maxn];
inline void add(int x,int y) {
    e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void dfs1(int x) {
    sum[x]=1;
    for(re int i=head[x];i;i=e[i].nxt) {
        if(deep[e[i].v]) continue;
        deep[e[i].v]=deep[x]+1,fa[e[i].v]=x;
        dfs1(e[i].v),sum[x]+=sum[e[i].v];
        if(sum[e[i].v]>sum[son[x]]) son[x]=e[i].v;
    }
}
void dfs2(int x,int topf) {
    top[x]=topf,st[x]=++__,dfn[__]=x;
    if(son[x]) dfs2(son[x],topf);
    for(re int i=head[x];i;i=e[i].nxt)
    if(!top[e[i].v]) dfs2(e[i].v,e[i].v);
    ed[x]=++__,dfn[__]=x;
}
inline int LCA(int x,int y) {
    while(top[x]!=top[y]) {
        if(deep[top[x]]<deep[top[y]]) std::swap(x,y);
        x=fa[top[x]];
    }
    if(deep[x]<deep[y]) return x;return y;
}
inline int cmp(Ask A,Ask B) {
    if(id[A.l]!=id[B.l]) return A.l<B.l;
    if(id[A.r]!=id[B.r]) return A.r<B.r;
    return A.t<B.t;
}
inline void Add(int x) {
    tmp[x]++;ans+=1ll*w[tmp[x]]*a[x];
}
inline void Del(int x) {
    ans-=1ll*w[tmp[x]]*a[x];tmp[x]--;
}
inline void add(int x) {
    if(tax[dfn[x]]==0) Add(col[dfn[x]]);
    if(tax[dfn[x]]==1) Del(col[dfn[x]]);
    tax[dfn[x]]++;
}
inline void del(int x) {
    tax[dfn[x]]--;
    if(tax[dfn[x]]==0) Del(col[dfn[x]]);
    if(tax[dfn[x]]==1) Add(col[dfn[x]]);
}
inline int check(int x,int l,int r) {
    if(st[x]>=l&&st[x]<=r&&ed[x]>=l&&ed[x]<=r) return 0;
    if(st[x]>=l&&st[x]<=r) return 1;
    if(ed[x]>=l&&ed[x]<=r) return 1;
    return 0;
}
inline void change(int now,int i) {
    if(check(pos[now],q[i].l,q[i].r)) {
        Del(col[pos[now]]);
        Add(val[now]);
    }
    std::swap(col[pos[now]],val[now]);
}
int main() {
    n=read(),m=read(),Q=read();
    for(re int i=1;i<=m;i++) a[i]=read();
    for(re int i=1;i<=n;i++) w[i]=read();
    for(re int x,y,i=1;i<n;i++) 
        x=read(),y=read(),add(x,y),add(y,x);
    deep[1]=1,dfs1(1),dfs2(1,1);
    for(re int i=1;i<=n;i++) col[i]=read();
    for(re int opt,x,y,i=1;i<=Q;i++) {
        opt=read();x=read(),y=read();
        if(opt) {
            if(st[x]>st[y]) std::swap(x,y);
            int lca=LCA(x,y);++cnt;
            if(lca==x) q[cnt].l=st[x],q[cnt].r=st[y];
                else q[cnt].l=ed[x],q[cnt].r=st[y],q[cnt].o=lca;
            q[cnt].rk=cnt;q[cnt].t=tot;
        }
        else pos[++tot]=x,val[tot]=y;
    }
    sz=pow(2*n,0.66666666);
    for(re int i=1;i<=n*2;i++) id[i]=(i-1)/sz+1;
    std::sort(q+1,q+cnt+1,cmp);
    int l=0,r=0,now=0;
    for(re int i=1;i<=cnt;i++) {
        while(r<q[i].r) add(++r);
        while(l>q[i].l) add(--l);
        while(r>q[i].r) del(r--);
        while(l<q[i].l) del(l++);
        while(now<q[i].t) change(++now,i);
        while(now>q[i].t) change(now--,i);
        Ans[q[i].rk]=ans;
        if(q[i].o) 
            Ans[q[i].rk]+=1ll*(w[tmp[col[q[i].o]]+1])*a[col[q[i].o]];
    }
    for(re int i=1;i<=cnt;i++) printf("%lld\n",Ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/asuldb/p/10681680.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值