[WC2013] 糖果公园

III.[WC2013] 糖果公园

树上莫队。

树上莫队是依照于一种特殊的序列:括号序列而进行的莫队。括号序列,具体而言,是 dfs 树的时候,到一个点就插入序列,离开一个点的时候再插入一次。这样,所有在树上 \(x\sim y\) 路径上出现的点,在括号序列上就是自 \(x\) 第一次出现的位置到 \(y\) 第一次出现的位置,这一段上所有出现过恰一次的值——出现过零次的值显然不在考虑范围之内,而出现过两次的值是进了子树又出来的。可以开一个 vis 数组来表示每个点出现了奇数次还是偶数次。

但需要注意的是,若 LCA 并非两端点之一,它就不会被考虑到路径上,因此需要特判;若 \(x\) 不是 LCA,它也不会被考虑,也需要特判。

然后剩下就是带修莫队板子了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int BBB=2200;
int n,m,q,a[100100],b[100100],c[100100],buc[100100],st[200100][20],fir[100100],LG[200100],tot,dep[100100],bra[200100],lef[200100],cnt,lim;
bool vis[100100];
ll res,ans[100100];
vector<int>v[100100];
void dfs(int x,int fa){
	st[++tot][0]=x,fir[x]=tot,dep[x]=dep[fa]+1,bra[++cnt]=x,lef[x]=cnt;
	for(auto y:v[x])if(y!=fa)dfs(y,x),st[++tot][0]=x;
	bra[++cnt]=x;
}
int MIN(int x,int y){return dep[x]<dep[y]?x:y;}
int LCA(int x,int y){
	x=fir[x],y=fir[y];
	if(x>y)swap(x,y);
	int k=LG[y-x+1];
	return MIN(st[x][k],st[y-(1<<k)+1][k]);
}
struct modify{int x,bef,aft;}r[100100];
struct query{
	int x,y,z,id;
	friend bool operator<(const query&u,const query&v){
		if(lef[u.x]/BBB!=lef[v.x]/BBB)return lef[u.x]<lef[v.x];
		if(lef[u.y]/BBB!=lef[v.y]/BBB)return lef[u.y]<lef[v.y];
		return u.z<v.z;
	}
}p[100100];
void Rev(int x){if(vis[x])res-=1ll*b[buc[c[x]]--]*a[c[x]];else res+=1ll*b[++buc[c[x]]]*a[c[x]];vis[x]^=1;}
int L,R,T;
void Tplus(){++T;if(vis[r[T].x])Rev(r[T].x),c[r[T].x]=r[T].aft,Rev(r[T].x);else c[r[T].x]=r[T].aft;}
void Tminus(){if(vis[r[T].x])Rev(r[T].x),c[r[T].x]=r[T].bef,Rev(r[T].x);else c[r[T].x]=r[T].bef;T--;}
int main(){
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=m;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
	dfs(1,0);
	for(int i=2;i<=tot;i++)LG[i]=LG[i>>1]+1;
	for(int j=1;j<=LG[tot];j++)for(int i=1;i+(1<<j)-1<=tot;i++)st[i][j]=MIN(st[i][j-1],st[i+(1<<(j-1))][j-1]);
//	for(int i=1;i<=cnt;i++)printf("%d ",bra[i]);puts("");
//	for(int i=1;i<=n;i++)printf("%d ",lef[i]);puts("");
	for(int i=1;i<=n;i++)scanf("%d",&c[i]); 
	for(int i=1,t,x,y;i<=q;i++){
		scanf("%d%d%d",&t,&x,&y);
		if(!t)r[++lim].x=x,r[lim].bef=c[x],r[lim].aft=c[x]=y,i--,q--;
		else{
			if(lef[x]>lef[y])swap(x,y);
			p[i].x=x,p[i].y=y,p[i].z=lim,p[i].id=i;
		}
	}
//	for(int i=1;i<=lim;i++)printf("(%d,%d,%d)\n",r[i].x,r[i].bef,r[i].aft);
//	for(int i=1;i<=q;i++)printf("[%d,%d,%d]\n",p[i].x,p[i].y,p[i].z);
	L=1,R=0,T=lim;
	sort(p+1,p+q+1);
	for(int i=1;i<=q;i++){
		while(L>lef[p[i].x])Rev(bra[--L]);
		while(R<lef[p[i].y])Rev(bra[++R]);
		while(L<lef[p[i].x])Rev(bra[L++]);
		while(R>lef[p[i].y])Rev(bra[R--]);
		while(T>p[i].z)Tminus();
		while(T<p[i].z)Tplus();
		int lca=LCA(p[i].x,p[i].y);
		if(lca!=p[i].x&&lca!=p[i].y)Rev(lca);
		if(p[i].x!=lca)Rev(p[i].x);
//		printf("A:%d\n",p[i].id);
		ans[p[i].id]=res;
		if(p[i].x!=lca)Rev(p[i].x);
		if(lca!=p[i].x&&lca!=p[i].y)Rev(lca);
	}
	for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
	return 0;
}

posted @ 2021-04-06 10:03  Troverld  阅读(62)  评论(0编辑  收藏  举报