树上莫队。
这道题既可以欧拉序莫队(也说是括号序),也可以树分块版树上莫队。
这里使用后者,因为好写。
首先,这道题显然是一个带修树上莫队,因为要维护 (cnt) ,在树上,还带单点修改。
于是我们可以考虑先树分块,把序列上的做法对应到树上就是每相邻 (sqrt{n}) 个点一个块了。
然后在 (dfs) 完了过后记得把最后剩下的点再分成最后一个块。
那么我们现在讨论如何从 ((u,v)) 转移到 ((u`,v`)) 。
我们发现,我们可以这样来做:(u) 和 (v) 分别往上跳到 (lca) 处,然后把沿途的路径上的边取反,然后 (u`) 和 (v`) 也一样做一遍。
这样完了之后,我们就可以发现我们还需要特判的是 (lca=(u,v)) 和 (lca=(u`,v`)) ,于是我们再把这两点单独拿出来取反即可。
那么剩下的就是带修莫队了。
实现要具体见代码,要注意的要点有这些:
(1.) (dfs) 的时候,在遍历完一个子节点后就判断。
(2.) (dfs) 完了记得要把剩下的全部拿出来构成单独的一个块。
(3.) 带修莫队记得要拿一个 (sta) 来存当前变化的颜色。
(4.) 可以先插入一个点作为初始化。
代码:
#include <bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
#define ll long long
const int N=1e5+5,INF=1e9+7;
struct Query{
int u,v,t,id;
Query(int u=0,int v=0,int t=0,int id=0):u(u),v(v),t(t),id(id){}
}Q[N];
struct Change{
int u,las,nex;
Change(int u=0,int las=0,int nex=0):u(u),las(las),nex(nex){}
}C[N];
int val[N],w[N],fa[N],dep[N],son[N],sta[N];
int siz[N],top[N],bl[N],a[N],sum[N];
bool vis[N];
int n,m,q,Top,idx,cnt1,cnt2,block;
ll res,Ans[N];
vector<int> vec[N];
void dfs1(int u,int f){
int now=Top;
sta[++Top]=u,fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;//压入栈和更新信息
for(auto v:vec[u]){
if(v==f) continue;
dfs1(v,u);
if(Top-now>block){//如果里面的点多于 B 个
idx++;//块编号
while(Top!=now) bl[sta[Top--]]=idx;//更新节点所属块
}
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
return ;
}
void dfs2(int u,int f){//树剖预处理
top[u]=f;
if(!son[u]) return ;
dfs2(son[u],f);
for(auto v:vec[u]){
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
return ;
}
inline int QueryLca(int u,int v){//查询lca
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}
if(dep[u]<dep[v]) return u;
return v;
}
inline bool cmp(Query a,Query b){//莫队排序
if(bl[a.u]==bl[b.u]){
if(bl[a.v]==bl[b.v]) return a.t<b.t;
return bl[a.v]<bl[b.v];
}
return bl[a.u]<bl[b.u];
}
inline void Add(int u){//更新添加u的影响
sum[u]++;
res+=1ll*w[sum[u]]*val[u];
return ;
}
inline void Del(int u){//更新删掉u的影响
res-=1ll*w[sum[u]]*val[u];
sum[u]--;
return ;
}
inline void Update(int u){//把u这个点取反的影响(用变不用,不用变用)
if(vis[u]) Del(a[u]),vis[u]=false;
else Add(a[u]),vis[u]=true;
return ;
}
inline void Modify(int u,int t){//把u这个点的颜色换成t的影响
if(vis[u]) Del(a[u]),Add(t);
a[u]=t;
return ;
}
inline void Move(int u,int v){//把u->v这条路径的更新了
if(dep[u]<dep[v]) swap(u,v);
while(dep[u]>dep[v]) Update(u),u=fa[u];
while(u!=v) Update(u),Update(v),u=fa[u],v=fa[v];
return ;
}
int main(){
read(n),read(m),read(q);
block=pow(n,2.0/3);
for(int i=1;i<=m;i++) read(val[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);
vec[u].push_back(v),vec[v].push_back(u);
}
for(int i=1;i<=n;i++) read(a[i]),sta[i]=a[i];//a是最初的颜色
for(int i=1;i<=q;i++){
int op,u,v;
read(op),read(u),read(v);
if(op==0) C[++cnt1]=Change(u,sta[u],v),sta[u]=v;//操作,sta是当前的颜色
else ++cnt2,Q[cnt2]=Query(u,v,cnt1,cnt2);//l,r,t,id
}
memset(sta,0,sizeof(sta));
dfs1(1,0),dfs2(1,1);//分块和LCA预处理
while(Top>0) bl[sta[Top--]]=idx;//分完块
sort(Q+1,Q+cnt2+1,cmp);//莫队排序
int u,v,t;
u=v=1,t=0;
Update(1);//初始化第一个点
for(int i=1;i<=cnt2;i++){//处理询问
while(t<Q[i].t) Modify(C[t+1].u,C[t+1].nex),t++;//修正时间
while(t>Q[i].t) Modify(C[t].u,C[t].las),--t;
Update(QueryLca(u,v));//两个LCA在这里要单独讨论
if(u!=Q[i].u) Move(u,Q[i].u),u=Q[i].u;//u更新到u`
if(v!=Q[i].v) Move(v,Q[i].v),v=Q[i].v;//v更新到v`
Update(QueryLca(u,v));//讨论
Ans[Q[i].id]=res;
}
for(int i=1;i<=cnt2;i++) write(Ans[i]),putchar('
');
return 0;
}