题意:
分析:
julao口中的树上带修莫队的板子题
- 前置芝士: 欧拉序,带修莫队
这个题拆开来说就是:带修莫队+树上莫队
- 带修莫队是莫队的最基本的一种,就是将询问排序后,按时间戳将修改操作增加或减少
- 树上莫队有两种写法,分别是按照大小分块和按照欧拉序分块,这里介绍用欧拉序分块的写法,通过欧拉序我们可以将树上问题转化为序列问题,因为欧拉序里两个相同的数之间包含ta的子树
tip:对于树上莫队的欧拉序写法还需要特别注意一下lca的特判,当\(lca(x,y)\ne x|y\)时,在欧拉序上是不会出现\(lca\)的,需要专门计算
代码:
#include<bits/stdc++.h>
using namespace std;
namespace zzc
{
typedef long long ll;
const int maxn = 5e5+5;
ll head[maxn],c[maxn],v[maxn],w[maxn],id[maxn],in[maxn],out[maxn],dep[maxn];
ll fa[maxn][25],belong[maxn],pre[maxn],cnt[maxn];
ll n,m,q,ecnt=0,qcnt=0,block,tim,ans=0;
bool vis[maxn];
struct edge
{
ll to,nxt;
}e[maxn<<1];
struct chan
{
ll pos,old,now,id;
}C[maxn];
struct que
{
ll l,r,val,id,ti;
}Q[maxn];
bool cmp1(que a,que b)
{
return belong[a.l]==belong[b.l]?belong[a.r]==belong[b.r]?a.ti<b.ti:belong[a.r]<belong[b.r]:belong[a.l]<belong[b.l];
}
bool cmp2(que a,que b)
{
return a.id<b.id;
}
void add(ll u,ll v)
{
e[++ecnt].to=v;
e[ecnt].nxt=head[u];
head[u]=ecnt;
}
ll idx;
void dfs(ll u,ll ff)
{
fa[u][0]=ff;
dep[u]=dep[ff]+1;
id[++idx]=u;
in[u]=idx;
for(ll i=head[u];i;i=e[i].nxt)
{
ll v=e[i].to;
if(v==ff) continue;
dfs(v,u);
}
id[++idx]=u;
out[u]=idx;
}
void getpre()
{
for(ll i=1;i<=20;i++)
{
for(ll j=1;j<=n;j++)
{
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
}
ll lca(ll x,ll y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(dep[fa[x][i]]>=dep[y])
{
x=fa[x][i];
}
}
if(x==y) return x;
for(int i=20;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
void calc(int pos)
{
if(vis[pos]) ans-=w[cnt[c[pos]]--]*v[c[pos]];
else ans+=w[++cnt[c[pos]]]*v[c[pos]];
vis[pos]^=1;
}
void update(int pos,int col)
{
if(vis[pos])
{
calc(pos);
c[pos]=col;
calc(pos);
}
else c[pos]=col;
}
void work()
{
ll a,b,opt;
scanf("%lld%lld%lld",&n,&m,&q);
block=(ll)pow(n,2.0/3);
for(ll i=1;i<=m;i++) scanf("%lld",&v[i]);
for(ll i=1;i<=n;i++) scanf("%lld",&w[i]);
for(ll i=1;i<n;i++)
{
scanf("%lld%lld",&a,&b);
add(a,b);
add(b,a);
}
for(ll i=1;i<=n;i++) scanf("%lld",&c[i]),pre[i]=c[i];
dfs(1,0);
getpre();
for(int i=1;i<=idx;i++)
{
belong[i]=(i-1)/block+1;
}
for(ll i=1;i<=q;i++)
{
scanf("%lld%lld%lld",&opt,&a,&b);
if(opt==1)
{
if(in[a]>in[b]) swap(a,b);
Q[++qcnt].id=qcnt;
Q[qcnt].r=in[b];
Q[qcnt].ti=tim;
Q[qcnt].l=((lca(a,b)==a)?in[a]:out[a]);
}
else
{
C[++tim].pos=a;
C[tim].old=pre[a];
C[tim].now=b;
pre[a]=b;
}
}
sort(Q+1,Q+qcnt+1,cmp1);
tim=0;
ll l=1,r=0;
for(int i=1;i<=qcnt;i++)
{
while(tim<Q[i].ti)
{
tim++;
update(C[tim].pos,C[tim].now);
}
while(tim>Q[i].ti)
{
update(C[tim].pos,C[tim].old);
tim--;
}
while(r<Q[i].r) calc(id[++r]);
while(l>Q[i].l) calc(id[--l]);
while(l<Q[i].l) calc(id[l++]);
while(r>Q[i].r) calc(id[r--]);
ll x=id[l],y=id[r],tmp=lca(x,y);
if(tmp!=x&&tmp!=y)
{
calc(tmp);
Q[i].val=ans;
calc(tmp);
}
else Q[i].val=ans;
}
sort(Q+1,Q+qcnt+1,cmp2);
for(int i=1;i<=qcnt;i++)
{
printf("%lld\n",Q[i].val);
}
}
}
int main()
{
zzc::work();
return 0;
}