bzoj1036: [ZJOI2008]树的统计Count [Link-Cut-Tree/树链剖分]

每次写LCT的过程都比较痛苦,这次也不例外,虽然花的时间不到一小时。

但是这样一道连反转标记都没有带的LCT写那么痛苦也着实不应该啊。

这道题要求支持一下操作:

        QMAX(a,b): 求点a,b路径上的最大权值

        QSUM(a,b): 求点a,b路径上的权值和

        CHANGE(a,b):将a点的权值修改为b

一看就是比较裸的LCT了,实际上连Link和Cut的操作都不用写,倒是更像单纯的树链剖分。

QMAX操作和QSUM操作类似,就是对两个点分别进行Access操作,然后就可以得到lca以及我们想要的信息。

当年Access的这个作用曾让我惊叹。。所以为了方便理解,我画了一下大概的图。

 

从图中我们看到:Access(u)让我们把从根节点到节点u的路径上的点并为同一树链。

而在下一次Access(v)的过程中,显然会把图中划去的边断开,断开的边旁边就是lca!

所以我们可以这么做:

        先Access(u),保证lca到根节点的路径上的点在一个树链中。

       再Access(v),此时该树链的根节点(树链在实现中是Splay树)显然应该就是lca,

       因为观察Access的过程我们可以发现,*每一次我们都会将当前点父节点Splay到树链根部并将当前节点作为它的右儿子。

       所以在Access(v)时,由于之前的Access(u),root->lca这条路上已经属于同一树链,所以在最后一次Splay时,我们实际

       上Splay的就是lca,为什么?请仔细思考*处。

于是做法也就比较清晰了,QMAX和QSUM其实类似,所以维护等操作都可以一起做。

码量虽然不多。。但也不算少,所以需要足够细心。

----------------------------------------------------------------------------------------------------------------------------------------------------------------

Code:

#include<iostream>
#include<cstdio>
using namespace std;

const int N=100100;
const int INF=~0u>>1;

#define L(p) T[T[p].s[0]]
#define R(p) T[T[p].s[1]]
#define F(p) T[T[p].f] 

int n; char opt[10];
struct BST{
	int maxv,sumv,v;
	int s[2],f,ws;
	BST(){
		sumv=0; maxv=-INF;
		s[0]=s[1]=f=ws=0;
	}
}T[N];
struct list{
	int p;
	list *next;
	list* Set(int _p,list* _next){
		p=_p; next=_next;
		return this;
	}
}E[N],*head[N];int edge=0;

bool isroot(int p){
	return (F(p).s[0]!=p&&F(p).s[1]!=p);
}

void MT(int p){
	if (!p) return;
	T[p].sumv=L(p).sumv+R(p).sumv+T[p].v;
	T[p].maxv=max(max(L(p).maxv,R(p).maxv),T[p].v);
}

void Sets(int f,int p,int ws){T[T[p].f=f].s[T[p].ws=ws]=p;}
	
void Rot(int p){
	int f=T[p].f,pf=F(p).f,ws=T[p].ws;
	if (!isroot(f)) Sets(pf,p,F(p).ws);else T[p].f=T[f].f;
	if (T[p].s[ws^1]) Sets(f,T[p].s[ws^1],ws);else T[f].s[ws]=0;
	Sets(p,f,ws^1);
	MT(f); MT(p); MT(pf);
}

void Splay(int p){
	for(;!isroot(p);Rot(p))
	if(!isroot(T[p].f)){
		if(F(p).ws==T[p].ws) Rot(T[p].f);
		else Rot(p);
	}
}

int Access(int x){
	int last=0;
	for(;x;x=T[x].f){
		Splay(x);		
		Sets(x,last,1);
		MT(last=x);
	}return last;
}
	
int qmax(int u,int v){
	Access(u);
	int lca=Access(v);
	Splay(u);
	if(lca==u) return max(T[lca].v,R(lca).maxv);
	else return max(T[lca].v,max(T[u].maxv,R(lca).maxv));
}
	
int qsum(int u,int v){
	Access(u);
	int lca=Access(v);
	Splay(u);
	if(lca==u) return T[lca].v+R(lca).sumv;
	else return (T[lca].v+R(lca).sumv+T[u].sumv);
}

void change(int p,int v){
	Splay(p);
	T[p].v=v;
	MT(p); 
} 

void Addedge(int u,int v){
	head[u]=(E+(edge++))->Set(v,head[u]);
	head[v]=(E+(edge++))->Set(u,head[v]); 
}

void dfs(int p,int f){
	T[p].f=f;
	for(list *t=head[p];t;t=t->next)
	if(t->p!=f) dfs(t->p,p);
}

int main(){
	freopen("1036.in","r",stdin);
	freopen("1036.out","w",stdout);
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		Addedge(u,v);
	}dfs(1,0);
	for(int i=1,v;i<=n;i++){
		scanf("%d",&v);
		T[i].v=T[i].maxv=T[i].sumv=v;
	}
	int a,b,m;
	scanf("%d",&m);
	while(m--){
		scanf("%s",opt);
		scanf("%d%d",&a,&b);	
		if(opt[1]=='H') change(a,b);
		else if(opt[1]=='S') printf("%d\n",qsum(a,b));
		else printf("%d\n",qmax(a,b));
	}
	return 0;
} 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值