糖果公园 [树上带修莫队]

链接

https://uoj.ac/problem/58

题意

n n n个节点的树, m m m种颜色, q q q个询问。
树上每个节点有一个颜色,每种颜色有一个权值。
i i i次遍历到某种颜色的点,该点的价值乘上对应的数值。
需要支持两种操作:
0 0 0 x x x y y y : 把 x x x点的颜色改为 y y y
1 1 1 x x x y y y : 查询遍历 x x x y y y路径的总价值。

分析

用树的括号序将查询的一条链转换成括号序列上的一个区间,再在括号序列上利用带修莫队求解。

将链转换成区间

处理出树的括号序,在对树进行 d f s dfs dfs 的时候,进入和离开某个点的时候都要打上标记,记 x x x点进入和离开的时间戳为 i d L [ x ] idL[x] idL[x] i d R [ x ] idR[x] idR[x]
将一条链转换成一个括号区间,要保证链上的点在区间中只出现一个括号,不在链上的点在区间上没有出现,或者出现的两个括号相互抵消。

对于一条端点为 x x x y y y 的链,假设 i d L [ x ] ≤ i d L [ y ] idL[x]≤idL[y] idL[x]idL[y] ,算出 x 和 y 的 L C A LCA LCA

  1. 如果 x x x y y y 就是 l c a lca lca ,那么这条链对应的括号序区间为 i d L [ x ] idL[x] idL[x] i d L [ y ] idL[y] idL[y]
  2. 如果 x x x y y y 都不是 l c a lca lca ,那么对应的区间为 i d R [ x ] idR[x] idR[x] i d L [ y ] idL[y] idL[y]。不过这个区间中并没有包含 l c a lca lca 这个点,这需要在处理询问的时候特别加上。
带修莫队

莫队的区间中第二次加入一个点时,改为删除该点,撤销该操作就是加入该点,可以标记该点的奇偶性来进行判断。
加上修改操作后同理,利用奇偶性判断是否修改。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double db;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
	x=0;char o,f=1;
	while(o=getchar(),o<48)if(o==45)f=-f;
	do x=(x<<3)+(x<<1)+(o^48);
	while(o=getchar(),o>47);
	x*=f;
}
const int M=1e5+5;
const int S=2000;
int n,m,q,A[M],B[M],C[M],tot,head[M],to[M<<1],nxt[M<<1];
inline void add_edge(int a,int b){
	to[++tot]=b;
	nxt[tot]=head[a];
	head[a]=tot;
}
int fa[M],son[M],sz[M],deep[M],top[M];
int reid[M<<1],idL[M],idR[M],dfsid;
void dfs(int x,int f){
	fa[x]=f;
	sz[x]=1;
	deep[x]=deep[f]+1;
	reid[idL[x]=++dfsid]=x;
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==f)continue;
		dfs(y,x);
		sz[x]+=sz[y];
		if(sz[y]>sz[son[x]])son[x]=y;
	}
	reid[idR[x]=++dfsid]=x;
}
void chain(int x,int k){
	top[x]=k;
	if(son[x])chain(son[x],k);
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa[x]||y==son[x])continue;
		chain(y,y);
	}
}
int LCA(int a,int b){
	while(top[a]!=top[b]){
		if(deep[top[a]]<deep[top[b]])swap(a,b);
		a=fa[top[a]];
	}
	return deep[a]<deep[b]?a:b;
}
struct node{
	int l,r,t,lca,id;
	bool operator <(const node &A)const{
		if(l/S!=A.l/S)return l/S<A.l/S;
		if(r/S!=A.r/S){
			if(l/S%2)return r/S<A.r/S;
			else return r/S>A.r/S;
		}
		if(r/S%2)return t<A.t;
		else return t>A.t;
	}
}Q[M];
int op0,op1,X[M],Y[M],Z[M],mark[M],cnt[M];
ll res,ans[M];
void add(int x){
	mark[x]^=1;
	if(mark[x])res+=1ll*A[C[x]]*B[++cnt[C[x]]];
	else res-=1ll*A[C[x]]*B[cnt[C[x]]--];
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("jiedai.in","r",stdin);
//	freopen("jiedai.out","w",stdout);
#endif
	rd(n),rd(m),rd(q);
	for(int i=1;i<=m;i++)rd(A[i]);
	for(int i=1;i<=n;i++)rd(B[i]);
	for(int i=1;i<n;i++){
		int a,b;
		rd(a),rd(b);
		add_edge(a,b),add_edge(b,a);
	}
	for(int i=1;i<=n;i++)rd(C[i]);
	dfs(1,0);
	chain(1,1);
	int now=0;
	for(int i=1;i<=q;i++){
		int op,x,y;
		rd(op),rd(x),rd(y);
		if(op==0){
			X[++op0]=x,Y[op0]=y,Z[op0]=C[x];
			C[x]=y,now++;
		}
		else{
			int lca=LCA(x,y);
			if(idL[x]>idL[y])swap(x,y);
			if(x==lca||y==lca)Q[++op1]=(node){idL[x],idL[y],op0,0,i};
			else Q[++op1]=(node){idR[x],idL[y],op0,lca,i};
		}
	}
	sort(Q+1,Q+1+op1);
	int L=Q[1].l,R=L-1;
	for(int i=1;i<=op1;i++){
		int l=Q[i].l,r=Q[i].r,t=Q[i].t,lca=Q[i].lca,id=Q[i].id;
		while(L>l)add(reid[--L]);
		while(R<r)add(reid[++R]);
		while(L<l)add(reid[L++]);
		while(R>r)add(reid[R--]);
		while(now<t){
			now++;
			if(mark[X[now]]){
				add(X[now]);
				C[X[now]]=Y[now];
				add(X[now]);
			}
			else C[X[now]]=Y[now];
		}
		while(now>t){
			if(mark[X[now]]){
				add(X[now]);
				C[X[now]]=Z[now];
				add(X[now]);
			}
			else C[X[now]]=Z[now];
			now--;
		}
		if(lca)add(lca);
		ans[id]=res;
		if(lca)add(lca);
	}
	for(int i=1;i<=q;i++)if(ans[i])printf("%lld\n",ans[i]);
	return (0-0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值