[WC2013]糖果公园

题目

总算知道欧拉序和树上莫队是个什么东西了

看到这个计算贡献的方式不禁想到了莫队

带修莫队非常\(naive\)就是多加一维时间

来讲讲树上莫队

如果我们需要子树莫队的话,我们可以直接用\(dfs\)序把子树转化成一段区间

但是树上的一条路径显然不是很方便这样做了

于是我们考虑使用欧拉序

欧拉序就是在\(dfs\)进入一个点的时候给它一个编号,记做\(st_x\),离开这个点的时候又给它一个编号,记做\(ed_x\)

这样就得到了一个长度为\(2n\)的括号序列

对于一条路径\((x,y)\),我们强行使得\(st_x<st_y\)

如果满足\(lca(x,y)=x\),那么对应的就是区间就是\([st_x,st_y]\),但是我们只计算这里面出现了一次的点,出现了两次或\(0\)次的点不在这条路径上

如果没有满足\(x\)\(y\)的祖先,对应的区间就是\([ed_x,st_y]\),这样就会发现\(lca\)\(x\)路径上的点只有\(ed\)出现在了这个区间里,\(lca\)\(y\)的路径上的点只有\(st\)出现在这个区间里,但是我们还需要特判一下\(lca\)

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define re register
#define LL long long
const int maxn=100005;
inline int read() {
	char c=getchar();int x=0;while(c<'0'||x>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
struct Ask{int l,r,o,rk,t;}q[maxn];
int head[maxn],son[maxn],sum[maxn],deep[maxn],col[maxn];
int ed[maxn],st[maxn],dfn[maxn<<1],top[maxn],fa[maxn],w[maxn];
int pos[maxn],val[maxn],id[maxn<<1],tmp[maxn],tax[maxn],a[maxn];
int n,m,num,sz,tot,cnt,__,Q;
LL ans,Ans[maxn];
inline void add(int x,int y) {
	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void dfs1(int x) {
	sum[x]=1;
	for(re int i=head[x];i;i=e[i].nxt) {
		if(deep[e[i].v]) continue;
		deep[e[i].v]=deep[x]+1,fa[e[i].v]=x;
		dfs1(e[i].v),sum[x]+=sum[e[i].v];
		if(sum[e[i].v]>sum[son[x]]) son[x]=e[i].v;
	}
}
void dfs2(int x,int topf) {
	top[x]=topf,st[x]=++__,dfn[__]=x;
	if(son[x]) dfs2(son[x],topf);
	for(re int i=head[x];i;i=e[i].nxt)
	if(!top[e[i].v]) dfs2(e[i].v,e[i].v);
	ed[x]=++__,dfn[__]=x;
}
inline int LCA(int x,int y) {
	while(top[x]!=top[y]) {
		if(deep[top[x]]<deep[top[y]]) std::swap(x,y);
		x=fa[top[x]];
	}
	if(deep[x]<deep[y]) return x;return y;
}
inline int cmp(Ask A,Ask B) {
	if(id[A.l]!=id[B.l]) return A.l<B.l;
	if(id[A.r]!=id[B.r]) return A.r<B.r;
	return A.t<B.t;
}
inline void Add(int x) {
	tmp[x]++;ans+=1ll*w[tmp[x]]*a[x];
}
inline void Del(int x) {
	ans-=1ll*w[tmp[x]]*a[x];tmp[x]--;
}
inline void add(int x) {
	if(tax[dfn[x]]==0) Add(col[dfn[x]]);
	if(tax[dfn[x]]==1) Del(col[dfn[x]]);
	tax[dfn[x]]++;
}
inline void del(int x) {
	tax[dfn[x]]--;
	if(tax[dfn[x]]==0) Del(col[dfn[x]]);
	if(tax[dfn[x]]==1) Add(col[dfn[x]]);
}
inline int check(int x,int l,int r) {
	if(st[x]>=l&&st[x]<=r&&ed[x]>=l&&ed[x]<=r) return 0;
	if(st[x]>=l&&st[x]<=r) return 1;
	if(ed[x]>=l&&ed[x]<=r) return 1;
	return 0;
}
inline void change(int now,int i) {
	if(check(pos[now],q[i].l,q[i].r)) {
		Del(col[pos[now]]);
		Add(val[now]);
	}
	std::swap(col[pos[now]],val[now]);
}
int main() {
	n=read(),m=read(),Q=read();
	for(re int i=1;i<=m;i++) a[i]=read();
	for(re int i=1;i<=n;i++) w[i]=read();
	for(re int x,y,i=1;i<n;i++) 
		x=read(),y=read(),add(x,y),add(y,x);
	deep[1]=1,dfs1(1),dfs2(1,1);
	for(re int i=1;i<=n;i++) col[i]=read();
	for(re int opt,x,y,i=1;i<=Q;i++) {
		opt=read();x=read(),y=read();
		if(opt) {
			if(st[x]>st[y]) std::swap(x,y);
			int lca=LCA(x,y);++cnt;
			if(lca==x) q[cnt].l=st[x],q[cnt].r=st[y];
				else q[cnt].l=ed[x],q[cnt].r=st[y],q[cnt].o=lca;
			q[cnt].rk=cnt;q[cnt].t=tot;
		}
		else pos[++tot]=x,val[tot]=y;
	}
	sz=pow(2*n,0.66666666);
	for(re int i=1;i<=n*2;i++) id[i]=(i-1)/sz+1;
	std::sort(q+1,q+cnt+1,cmp);
	int l=0,r=0,now=0;
	for(re int i=1;i<=cnt;i++) {
		while(r<q[i].r) add(++r);
		while(l>q[i].l) add(--l);
		while(r>q[i].r) del(r--);
		while(l<q[i].l) del(l++);
		while(now<q[i].t) change(++now,i);
		while(now>q[i].t) change(now--,i);
		Ans[q[i].rk]=ans;
		if(q[i].o) 
			Ans[q[i].rk]+=1ll*(w[tmp[col[q[i].o]]+1])*a[col[q[i].o]];
	}
	for(re int i=1;i<=cnt;i++) printf("%lld\n",Ans[i]);
	return 0;
}
posted @ 2019-04-10 10:19  asuldb  阅读(171)  评论(0编辑  收藏  举报