[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;
}