[SPOJ]QTREE7

题意:有一棵树,节点有黑白两种颜色,带点权,三种操作:反转颜色,修改点权,询问同色连通块中的最大点权

得温习一下lct了...

维护黑白两棵lct,multiset维护子树信息即可,反转颜色就是一种颜色cut另一种颜色link

根的处理比较麻烦,但我们可以在建lct的时候让每棵lct中含有最多一个非同色点(并且是深度最浅的),查询时特判一下即可,这样做的好处就是翻转根的颜色时不用特判

#include<stdio.h>
#include<set>
using namespace std;
const int inf=2147483647;
int c[100010];
struct lct{
	int fa[100010],ch[100010][2],v[100010],mx[100010];
	multiset<int>ms[100010];
	#define ls ch[x][0]
	#define rs ch[x][1]
	lct(){mx[0]=-inf;}
	int vmax(int x){return ms[x].empty()?-inf:*ms[x].rbegin();}
	void pushup(int x){
		mx[x]=max(max(mx[ls],mx[rs]),max(v[x],vmax(x)));
	}
	void rot(int x){
		int y,z,f,b;
		y=fa[x];
		z=fa[y];
		f=ch[y][0]==x;
		b=ch[x][f];
		fa[x]=z;
		fa[y]=x;
		if(b)fa[b]=y;
		ch[x][f]=y;
		ch[y][f^1]=b;
		if(ch[z][0]==y)ch[z][0]=x;
		if(ch[z][1]==y)ch[z][1]=x;
		pushup(y);
		pushup(x);
	}
	bool isrt(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
	void splay(int x){
		int y,z;
		while(!isrt(x)){
			y=fa[x];
			z=fa[y];
			if(!isrt(y))rot((ch[z][0]==y&&ch[y][0]==x)||(ch[z][1]==y&&ch[y][1]==x)?y:x);
			rot(x);
		}
	}
	void access(int x){
		int y=0;
		while(x){
			splay(x);
			if(rs)ms[x].insert(mx[rs]);
			rs=y;
			if(y)ms[x].erase(ms[x].find(mx[y]));
			pushup(x);
			y=x;
			x=fa[x];
		}
	}
	void link(int x,int y){//x=fa[y]
		access(x);
		splay(x);
		splay(y);
		fa[y]=x;
		rs=y;
		pushup(x);
	}
	void cut(int x){
		access(x);
		splay(x);
		fa[ls]=0;
		ls=0;
		pushup(x);
	}
	void modify(int x,int d){
		access(x);
		splay(x);
		v[x]=d;
		pushup(x);
	}
	int getrt(int x){
		access(x);
		splay(x);
		while(ls)x=ls;
		return x;
	}
	int query(int x){
		int f=c[x];
		splay(x=getrt(x));
		return f==c[x]?mx[x]:mx[rs];
	}
}t[2];
int h[100010],nex[200010],to[200010],fa[100010],M;
void add(int a,int b){
	M++;
	to[M]=b;
	nex[M]=h[a];
	h[a]=M;
}
void dfs(int x){
	for(int i=h[x];i;i=nex[i]){
		if(to[i]!=fa[x]){
			fa[to[i]]=x;
			t[c[to[i]]].fa[to[i]]=x;
			dfs(to[i]);
			t[c[to[i]]].ms[x].insert(t[c[to[i]]].mx[to[i]]);
		}
	}
	t[0].pushup(x);
	t[1].pushup(x);
}
int main(){
	int n,m,i,x,y;
	scanf("%d",&n);
	for(i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	for(i=1;i<=n;i++)scanf("%d",c+i);
	for(i=1;i<=n;i++){
		scanf("%d",&x);
		t[0].v[i]=t[1].v[i]=x;
	}
	dfs(1);
	scanf("%d",&m);
	while(m--){
		scanf("%d%d",&i,&x);
		if(i==0)printf("%d\n",t[c[x]].query(x));
		if(i==1){
			if(fa[x])t[c[x]].cut(x);
			c[x]^=1;
			if(fa[x])t[c[x]].link(fa[x],x);
		}
		if(i==2){
			scanf("%d",&y);
			t[0].modify(x,y);
			t[1].modify(x,y);
		}
	}
}

转载于:https://www.cnblogs.com/jefflyy/p/9043118.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值