解题思路:
传说中的b站毒瘤题,一交就引起民粪。
本来只是想做树上莫队模板bzoj3757苹果树的,结果题目下架了,只能上这道了……
如果不会树分块的可以先去做做bzoj1086,题解在这里
不会带修改莫队的可以先去做做bzoj2120,题解在这里
这里主要说一下正常无修改的树上莫队的做法。
先树分块,将每个询问按左端点所在块为第一关键字,右端点dfs序为第二关键字排序,这样就保证了指针挪动复杂度(证明方法同普通莫队)。
当前已知区间(x,y)表示x到y路径上除了lca(x,y)的所有点信息,对于每个节点记一个它是否已知的标记(lca标记为未知),这样对于下一个询问(tarx,tary),我们只需暴力把x指针挪到tarx,把y指针挪到tary,维护答案,并将路径上的标记取反,那么新的已知区间就是tarx到tary路径上除了lca(tarx,tary)的所有点了,所以单独算算lca的贡献即可。
不清楚的同学可以画画图手完一下,很明显的,详细证明可见这里
所以这道题我们在树上莫队的基础上套一个带修改莫队,排序第二关键字改为右端点所在块,修改是否在当前区间内可用x的标记直接判断。
细节见代码,还是很好懂的。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005;
int n,m,Q,S,qn,num,top;
int tot,first[N],dep[N],fa[N][20],nxt[N<<1],to[N<<1];
int c[N],belong[N],cnt[N],V[N],stk[N];
ll cur,W[N],ans[N];
bool exist[N];
struct node
{
int x,y,bl,br,id;
inline friend bool operator < (const node &a,const node &b)
{
if(a.bl!=b.bl)return a.bl<b.bl;
if(a.br!=b.br)return a.br<b.br;
return a.id<b.id;
}
}q[N];
struct node1
{
int x,val,ori;
}d[N];
void add(int x,int y)
{
nxt[++tot]=first[x],first[x]=tot,to[tot]=y;
}
void dfs(int u)
{
int now=top;
for(int i=1;i<20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(v==fa[u][0])continue;
fa[v][0]=u,dep[v]=dep[u]+1;
dfs(v);
if(top-now>=S)
{
++num;
while(top!=now)belong[stk[top--]]=num;
}
}
stk[++top]=u;
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
int det=dep[x]-dep[y];
for(int i=0;i<20;i++)if(det&(1<<i))x=fa[x][i];
for(int i=19;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return x==y?x:fa[x][0];
}
void rev(int x)
{
cur-=W[cnt[c[x]]]*V[c[x]];
exist[x]?--cnt[c[x]]:++cnt[c[x]];
exist[x]=!exist[x];
cur+=W[cnt[c[x]]]*V[c[x]];
}
void modify(int x,int y)
{
if(!exist[x]){c[x]=y;return;}
rev(x),c[x]=y,rev(x);
}
void upt(int curT,int tarT)
{
while(curT<tarT)
{
++curT;
if(d[curT].x)modify(d[curT].x,d[curT].val);
}
while(curT>tarT)
{
if(d[curT].x)modify(d[curT].x,d[curT].ori);
--curT;
}
}
void solve(int x,int y)
{
int t=lca(x,y);
while(x!=t)rev(x),x=fa[x][0];
while(y!=t)rev(y),y=fa[y][0];
}
int main()
{
//freopen("lx.in","r",stdin);
int op,x,y;
n=getint(),m=getint(),Q=getint();S=pow(n,2.0/3);
for(int i=1;i<=m;i++)V[i]=getint();
for(int i=1;i<=n;i++)W[i]=getint()+W[i-1];
for(int i=1;i<n;i++)
{
x=getint(),y=getint();
add(x,y),add(y,x);
}
for(int i=1;i<=n;i++)c[i]=getint();
dfs(1);
for(int i=1;i<=Q;i++)
{
op=getint(),x=getint(),y=getint();
if(op)
{
q[++qn].id=i,q[qn].x=x,q[qn].y=y,q[qn].bl=belong[x],q[qn].br=belong[y];
if(belong[x]>belong[y])swap(q[qn].x,q[qn].y),swap(q[qn].bl,q[qn].br);
}
else d[i].x=x,d[i].val=y,d[i].ori=c[x],c[x]=y;
}
sort(q+1,q+qn+1);q[0].x=q[0].y=1,q[0].id=Q+1;
for(int i=1;i<=qn;i++)
{
upt(q[i-1].id,q[i].id);
solve(q[i-1].x,q[i].x),solve(q[i-1].y,q[i].y);
int t=lca(q[i].x,q[i].y);
rev(t),ans[q[i].id]=cur,rev(t);
}
for(int i=1;i<=Q;i++)
if(!d[i].x)printf("%lld\n",ans[i]);
return 0;
}