链接
小·清·新 莫队题。
询问和区间内某一种数字的出现次数有关,而且还带修,那大概率就是莫队了。
但是我们发现这次是链查。而莫队是序列算法,不能在树上移指针。
所以我们要尝试化树为序列。所以个人感觉叫“树上莫队”是挂羊头卖狗肉。
总结常用的树序列和性质:
- dfs序(第一次访问时记录):一个子树内的dfs序为一个连续区间,且一个非叶子节点的某个儿子和它dfs序连续。
- 欧拉序(访问一次记录一次):一条链 (u ightarrow v) 上的所有点都在 ([l_u,l_v]) 上出现过,且 (u,v) 的非链上的祖先没有出现过。
- 括号序(第一次/最后一次访问时记录):一个点 (u) 到根路径上的所有点在 ([1,l_u]) 中恰好出现一次。
显然,这题用括号序更加妥当。
但是括号序的性质是一个点 (u) 到根路径上的点出现一次。那么链 (u ightarrow v)(假设 (l_u<l_v))应该对应 ((l_u,l_v])。
但是手算一下,发现他们的lca也被除掉了。所以再加上就好了。
那么这样我们就把树转换成序列问题。接下来我们只要统计区间内出现两次的数字即可。这个对于莫队来说没有什么难处。
那么接下来就是处理修改了。这个也就是套一个带修莫队就行了。
具体来说,我们需要给每个询问多加一维 (t),即它的统计是在第 (t) 个修改操作后。
然后我们在移动指针的时候,将修改的指针移动也加进去。即如果一个修改操作在当前询问区间内那么处理修改的贡献,然后再修改。
理论复杂度 (O(n^{frac 5 3})),但是莫队的复杂度你也信?
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 800010
#define ll long long
using namespace std;
int nxt[N<<1],to[N<<1],head[N],cnt;
void add(int u,int v)
{
nxt[++cnt]=head[u];
to[cnt]=v;
head[u]=cnt;
}
int ld[N],rd[N];
int qt[N],tot;
int dep[N],fa[N],son[N],siz[N];
int top[N];
void dfs1(int u,int p)
{
dep[u]=dep[p]+1;
fa[u]=p;
siz[u]=1;
qt[ld[u]=++tot]=u;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa[u]) continue;
dfs1(v,u);
if(siz[son[u]]<siz[v]) son[u]=v;
siz[u]+=siz[v];
}
qt[rd[u]=++tot]=u;
}
void dfs2(int u,int topp)
{
top[u]=topp;
if(son[u]) dfs2(son[u],topp);
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(v!=fa[u] && v!=son[u]) dfs2(v,v);
}
}
int lca(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
int cx[N],cy[N],cp[N],bl[N];
struct node{
int l,r,t,id,ex;
bool operator <(const node a)const
{
if(bl[l]!=bl[a.l]) return bl[l]<bl[a.l];
if(bl[r]!=bl[a.r]) return bl[r]<bl[a.r];
return t<a.t;
}
}q[N];
int qtot;
ll w[N],val[N],ans[N];
int vis[N],c[N];
ll wcnt[N],res;
int fl=1,fr=0,ft=0;
inline void ins(int x){wcnt[x]++; res+=val[x]*w[wcnt[x]];}
inline void ers(int x){res-=val[x]*w[wcnt[x]]; wcnt[x]--;}
inline void add(int x){vis[x]?ers(c[x]):ins(c[x]); vis[x]^=1;}
inline void del(int x){vis[x]^=1; vis[x]?ins(c[x]):ers(c[x]);}
inline void addq(int x)
{
if(vis[cx[x]]) ers(c[cx[x]]);
c[cx[x]]=cy[x];
if(vis[cx[x]]) ins(c[cx[x]]);
}
inline void delq(int x)
{
if(vis[cx[x]]) ers(c[cx[x]]);
c[cx[x]]=cp[x];
if(vis[cx[x]]) ins(c[cx[x]]);
}
int main()
{
int n,k,m;
scanf("%d%d%d",&n,&k,&m);
for(int i=1;i<=k;i++) scanf("%lld",&val[i]);
for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
int tm=0;
for(int i=1;i<=m;i++)
{
int opt,l,r;
scanf("%d%d%d",&opt,&l,&r);
if(opt==1)
{
if(ld[l]>ld[r]) swap(l,r);
int lc=lca(l,r);
if(lc==l) q[++qtot]=(node){ld[l],ld[r],tm,i,0};
else q[++qtot]=(node){rd[l],ld[r],tm,i,lc};
}
else
{
++tm;
cx[tm]=l,cy[tm]=r;
}
}
for(int i=1;i<=tm;i++) cp[i]=c[cx[i]],c[cx[i]]=cy[i];
for(int i=tm;i>=1;i--) c[cx[i]]=cp[i];
const int B=pow(n,2.0/3)*0.5+1;
for(int i=1;i<=3*n;i++) bl[i]=i/B;
sort(q+1,q+qtot+1);
for(int i=1;i<=qtot;i++)
{
while(fr<q[i].r) add(qt[++fr]);
while(fr>q[i].r) del(qt[fr--]);
while(fl<q[i].l) del(qt[fl++]);
while(fl>q[i].l) add(qt[--fl]);
while(ft<q[i].t) addq(++ft);
while(ft>q[i].t) delq(ft--);
if(q[i].ex) add(q[i].ex);
ans[q[i].id]=res;
if(q[i].ex) del(q[i].ex);
}
for(int i=1;i<=m;i++)
if(ans[i]) printf("%lld
",ans[i]);
return 0;
}