链接
https://uoj.ac/problem/58
题意
n
n
n个节点的树,
m
m
m种颜色,
q
q
q个询问。
树上每个节点有一个颜色,每种颜色有一个权值。
第
i
i
i次遍历到某种颜色的点,该点的价值乘上对应的数值。
需要支持两种操作:
0
0
0
x
x
x
y
y
y : 把
x
x
x点的颜色改为
y
y
y
1
1
1
x
x
x
y
y
y : 查询遍历
x
x
x到
y
y
y路径的总价值。
分析
用树的括号序将查询的一条链转换成括号序列上的一个区间,再在括号序列上利用带修莫队求解。
将链转换成区间
处理出树的括号序,在对树进行
d
f
s
dfs
dfs 的时候,进入和离开某个点的时候都要打上标记,记
x
x
x点进入和离开的时间戳为
i
d
L
[
x
]
idL[x]
idL[x] 和
i
d
R
[
x
]
idR[x]
idR[x]。
将一条链转换成一个括号区间,要保证链上的点在区间中只出现一个括号,不在链上的点在区间上没有出现,或者出现的两个括号相互抵消。
对于一条端点为 x x x 和 y y y 的链,假设 i d L [ x ] ≤ i d L [ y ] idL[x]≤idL[y] idL[x]≤idL[y] ,算出 x 和 y 的 L C A LCA LCA。
- 如果 x x x 或 y y y 就是 l c a lca lca ,那么这条链对应的括号序区间为 i d L [ x ] idL[x] idL[x]到 i d L [ y ] idL[y] idL[y]。
- 如果 x x x 和 y y y 都不是 l c a lca lca ,那么对应的区间为 i d R [ x ] idR[x] idR[x] 到 i d L [ y ] idL[y] idL[y]。不过这个区间中并没有包含 l c a lca lca 这个点,这需要在处理询问的时候特别加上。
带修莫队
莫队的区间中第二次加入一个点时,改为删除该点,撤销该操作就是加入该点,可以标记该点的奇偶性来进行判断。
加上修改操作后同理,利用奇偶性判断是否修改。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
x=0;char o,f=1;
while(o=getchar(),o<48)if(o==45)f=-f;
do x=(x<<3)+(x<<1)+(o^48);
while(o=getchar(),o>47);
x*=f;
}
const int M=1e5+5;
const int S=2000;
int n,m,q,A[M],B[M],C[M],tot,head[M],to[M<<1],nxt[M<<1];
inline void add_edge(int a,int b){
to[++tot]=b;
nxt[tot]=head[a];
head[a]=tot;
}
int fa[M],son[M],sz[M],deep[M],top[M];
int reid[M<<1],idL[M],idR[M],dfsid;
void dfs(int x,int f){
fa[x]=f;
sz[x]=1;
deep[x]=deep[f]+1;
reid[idL[x]=++dfsid]=x;
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==f)continue;
dfs(y,x);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]])son[x]=y;
}
reid[idR[x]=++dfsid]=x;
}
void chain(int x,int k){
top[x]=k;
if(son[x])chain(son[x],k);
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==fa[x]||y==son[x])continue;
chain(y,y);
}
}
int LCA(int a,int b){
while(top[a]!=top[b]){
if(deep[top[a]]<deep[top[b]])swap(a,b);
a=fa[top[a]];
}
return deep[a]<deep[b]?a:b;
}
struct node{
int l,r,t,lca,id;
bool operator <(const node &A)const{
if(l/S!=A.l/S)return l/S<A.l/S;
if(r/S!=A.r/S){
if(l/S%2)return r/S<A.r/S;
else return r/S>A.r/S;
}
if(r/S%2)return t<A.t;
else return t>A.t;
}
}Q[M];
int op0,op1,X[M],Y[M],Z[M],mark[M],cnt[M];
ll res,ans[M];
void add(int x){
mark[x]^=1;
if(mark[x])res+=1ll*A[C[x]]*B[++cnt[C[x]]];
else res-=1ll*A[C[x]]*B[cnt[C[x]]--];
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
// freopen("jiedai.out","w",stdout);
#endif
rd(n),rd(m),rd(q);
for(int i=1;i<=m;i++)rd(A[i]);
for(int i=1;i<=n;i++)rd(B[i]);
for(int i=1;i<n;i++){
int a,b;
rd(a),rd(b);
add_edge(a,b),add_edge(b,a);
}
for(int i=1;i<=n;i++)rd(C[i]);
dfs(1,0);
chain(1,1);
int now=0;
for(int i=1;i<=q;i++){
int op,x,y;
rd(op),rd(x),rd(y);
if(op==0){
X[++op0]=x,Y[op0]=y,Z[op0]=C[x];
C[x]=y,now++;
}
else{
int lca=LCA(x,y);
if(idL[x]>idL[y])swap(x,y);
if(x==lca||y==lca)Q[++op1]=(node){idL[x],idL[y],op0,0,i};
else Q[++op1]=(node){idR[x],idL[y],op0,lca,i};
}
}
sort(Q+1,Q+1+op1);
int L=Q[1].l,R=L-1;
for(int i=1;i<=op1;i++){
int l=Q[i].l,r=Q[i].r,t=Q[i].t,lca=Q[i].lca,id=Q[i].id;
while(L>l)add(reid[--L]);
while(R<r)add(reid[++R]);
while(L<l)add(reid[L++]);
while(R>r)add(reid[R--]);
while(now<t){
now++;
if(mark[X[now]]){
add(X[now]);
C[X[now]]=Y[now];
add(X[now]);
}
else C[X[now]]=Y[now];
}
while(now>t){
if(mark[X[now]]){
add(X[now]);
C[X[now]]=Z[now];
add(X[now]);
}
else C[X[now]]=Z[now];
now--;
}
if(lca)add(lca);
ans[id]=res;
if(lca)add(lca);
}
for(int i=1;i<=q;i++)if(ans[i])printf("%lld\n",ans[i]);
return (0-0);
}