bzoj3159: 决战 树链剖分+splay

方法很简单,树剖,把区间提取出来,打翻转标记,再放回去。

注意:由于某种原因,我写的是把题目中的r忽略掉的一般情况,否则简单得多。

于是本以为写起来也很简单ovo

结果发现非常繁琐,最后写得又长跑得又慢。

#include<bits/stdc++.h>
#define L(t) (t)->c[0]
#define R(t) (t)->c[1]
#define Z(t) (L(t)->s+1)
#define N 50005
#define M (s+t>>1)
#define u first
#define v second
#define int long long
using namespace std;
struct edge{
	edge* s;
	int v;
}e[N*2],*back(e),*h[N];
void add(int u,int v){
	h[u]=&(*back++
	=(edge){h[u],v});
	h[v]=&(*back++
	=(edge){h[v],u});
}
typedef int ds[N];
ds d,p,num,size,son,top;
void dfs1(int u){
	d[u]=d[p[u]]+1;
	size[u]=1;
	int s=0;
	for(edge* i=h[u];i;i=i->s)
		if(i->v!=p[u]){
			p[i->v]=u;
			dfs1(i->v);
			size[u]+=size[i->v];
			if(s<size[i->v])
				s=size[son[u]=i->v];
		}
}
void dfs2(int u){
	static int tot;
	num[u]=++tot;
	if(size[u]!=1){
		top[son[u]]=top[u];
		dfs2(son[u]);
	}
	for(edge* i=h[u];i;i=i->s)
		if(i->v!=p[u]
		&&i->v!=son[u])
			dfs2(top[i->v]=i->v);
}
struct node{
	int a[3],s,u,v;
	bool rev;
	node *c[2];
}tin[N],*next=tin,
zinc={0,1e9},
*null=&zinc,*root;
int Sum(int u,int v){
	return u+v;
}
int Min(int u,int v){
	return u<v?u:v;
}
int Max(int u,int v){
	return u<v?v:u;
}
int(*f[3])(int,int)
={Sum,Min,Max};
int devolve(node* t){
	if(t==null)
		return 0;
	if(t->rev){
		L(t)->rev^=1;
		R(t)->rev^=1;
		t->rev=0;
		swap(L(t),R(t));
	}
	if(int u=t->u){
		t->u=0;
		L(t)->u+=u;
		R(t)->u+=u;
		t->a[1]+=u;
		t->a[2]+=u;
		t->v+=u;
		t->a[0]+=u*t->s;
	}
	return Z(t);
}
node* update(node* t){
	devolve(L(t));
	devolve(R(t));
	for(int i=0;i!=3;++i)
		t->a[i]=f[i](f[i](
		L(t)->a[i],
		R(t)->a[i]),t->v);
	t->s=R(t)->s+Z(t);
	return t;
}
void link(bool i,
node*& t,node*& s){
	node* d=t->c[i];
	t->c[i]=s;
	s=update(t),t=d;
}
node* splay(int v,
node*& t=root){
	node* d[]={null,null};
	while(v!=devolve(t)){
		bool i=v>Z(t);
		v-=i*Z(t);
		if(v!=devolve(t->c[i])
		&&i==v>Z(t->c[i])){
			v-=i*Z(t->c[i]);
			link(i,t,
			t->c[i]->c[i^1]);
		}
		link(i,t,d[i]);
	}
	for(int i=0;i!=2;++i){
		node* s=t->c[i^1];
		while(d[i]!=null)
			link(i,d[i],s);
		t->c[i^1]=s;
	}
	return update(t);
}
node*& splay(int s,int t){
	splay(s);
	return L(splay(
	t-s+2,R(root)));
}
node* build(int s,int t){
	if(s<=t){
		node* i=next++;
		L(i)=build(s,M-1);
		R(i)=build(M+1,t);
		return update(i);
	}
	return null;
}
typedef pair<int,int> vec;
vec* u=new vec[N];
vec* v=new vec[N];
int solve(int i,int j,
vec*& u,vec*& v){
	vec **s=&u,**t=&v;
	for(;top[i]!=top[j];
	i=p[top[i]]){
		if(d[top[i]]<d[top[j]])
			swap(i,j),
			swap(s,t);
		*(*s)++=vec(
		num[top[i]],num[i]);
	}
	if(d[i]<d[j])
		swap(i,j),
		swap(s,t);
	*(*s)++=vec(num[j],num[i]);
	return num[j];
}
void amend(int p,int q,int j){
	vec *s=u,*t=v;
	solve(p,q,s,t);
	while(s--!=u)
		splay(
		s->u,s->v)->u+=j;
	while(t--!=v)
		splay(
		t->u,t->v)->u+=j;
}
int query(int p,int q,int i){
	int j=i^1?0:1e9;
	vec *s=u,*t=v;
	solve(p,q,s,t);
	while(s--!=u)
		j=f[i](j,splay(
		s->u,s->v)->a[i]);
	while(t--!=v)
		j=f[i](j,splay(
		t->u,t->v)->a[i]);
	return j;
}
void invert(int p,int q){
	int f=0;
	node **x,*j=null,*k=null;
	vec *y,*s=u,*t=v;
	int e=solve(p,q,s,t);
	if(u!=s&&v!=t&&u->u<v->v)
		swap(u,v),
		swap(s,t);
	for(vec* i=u;i!=s;++i)
		if(i->u==e||i->v==e)
			x=&j,y=i;
		else{
			f+=i->v-i->u+1;
			node*& a=splay(
			i->u,i->v);
			R(splay(a->s,a))=j;
			update(j=a),a=null;
		}
	for(vec* i=v;i!=t;++i)
		if(i->u==e||i->v==e)
			x=&k,y=i;
		else{
			node*& a=splay(
			i->u,i->v);
			R(splay(a->s,a))=k;
			update(k=a),a=null;
		}
	if(vec* i=y){
		if(x==&j)
			f+=i->v-i->u+1;
		node*& a=splay(
		i->u,i->v);
		R(splay(a->s,a))=*x;
		update(*x=a),a=null;
	}
	if(!f)k->rev=1;
	else{
		j->rev=1;
		R(splay(j->s,j))=k;
		update(j)->rev=1;
		k=R(splay(f,j));
		R(j)=null;
		update(j)->rev=1;
	}
	if(vec* i=y){
		node*& a=splay(
		i->u,i->u-1);
		*x=R(splay(i->v
		-i->u+1,a=*x));
		R(a)=null,update(a);
	}
	for(vec* i=t-1;i>=v;--i)
		if(i!=y){
			node*& a=splay(
			i->u,i->u-1);
			k=R(splay(i->v
			-i->u+1,a=k));
			R(a)=null,update(a);
		}
	for(vec* i=s-1;i>=u;--i)
		if(i!=y){
			node*& a=splay(
			i->u,i->u-1);
			j=R(splay(i->v
			-i->u+1,a=j));
			R(a)=null,update(a);
		}
}
#undef int
int main(){
	int n,m,s,t,u;
	char a[10];
	scanf("%d%d%*d",&n,&m);
	root=build(0,n+1);
	for(int i=1;i!=n;++i){
		scanf("%d%d",&s,&t);
		add(s,t);
	}
	dfs1(1);
	dfs2(top[1]=1);
	while(m--){
		scanf("%s%d%d",a,&s,&t);
		switch(a[2]){
		case‘v‘:
			invert(s,t);
			break;
		case‘c‘:
			scanf("%d",&u);
			amend(s,t,u);
			break;
		default:
			printf("%lld\n",
			query(s,t,a[2]
			==‘j‘?2:a[2]!=‘m‘));
		}
	}
}

  

时间: 2024-10-10 04:53:45

bzoj3159: 决战 树链剖分+splay的相关文章

SPOJ GSS系列 最大子段和 线段树+树链剖分+splay 1043 1557 1716 2713 2916 4487 6779

最大子段和的各种形式 题解内附每道题的 题意 题目链接 思路 SPOJ 1043 GSS1 静态区间求个最大子段和, 题解 SPOJ 1577 GSS2 和1一样,区别是若区间内存在相同的元素,则该元素只计算一次. 离线一下然后使劲跑.. 题解 SPOJ 1716 GSS3 和1一样,就是要支持单点修改 题解 SPOJ 2713 GSS4 ==普通线段树,感觉和这系列关系不大. 题解 SPOJ 2916 GSS5 题意有点怪,,跟3差不多,就是稍加强了点询问 题解 SPOJ 4487 GSS6

树链剖分简(单)介(绍)

树链剖分可以算是一种数据结构(一大堆数组,按照这个意思,主席树就是一大堆线段树).将一棵树分割成许多条连续的树链,方便完成一下问题: 单点修改(dfs序可以完成) 求LCA(各种乱搞也可以) 树链修改(修改任意树上两点之间的唯一路径) 树链查询 (各种操作)  前两个内容可以用其他方式解决,但是下面两种操作倍增.st表,dfs序就很难解决(解决当然可以解决,只是耗时长点而已).下面开始步入正题. 树链剖分的主要目的是分割树,使它成一条链,然后交给其他数据结构(如线段树,Splay)来进行维护.常

BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

数据结构之树链剖分

首先了解一下基本概念: 重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子.      轻儿子:v的其它子节点.      重边:点v与其重儿子的连边.      轻边:点v与其轻儿子的连边.      重链:由重边连成的路径.      轻链:轻边. 剖分后的树有如下性质:      性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v]:      性质2:从根到某一点的路径上轻链.重链的个数都不大于logN. 树链剖分,如其字面意思,就是将一棵树按照轻重

bzoj1036 count 树链剖分或LCT

这道题很久以前用树链剖分写的,最近在学LCT ,就用LCT再写了一遍,也有一些收获. 因为这道题点权可以是负数,所以在update时就要注意一下,因为平时我的0节点表示空,它的点权为0,这样可以处理点权为非负求最大值和求和的情况(即不用特判某个点是否有左右儿子,直接更新就行了),但这道题就不行(求和要求它为0,求最大值要求它为-oo).所以就必须特判~~~~ 综上:若0号节点存在一个不可能成为答案(在求最大值时是-oo,求最小值时是+oo)或对答案没有贡献的值(在求和时是0)时,初始化时将0节点

树链剖分原理

一.相关定义 树链剖分:把一棵树剖分为若干条链,然后利用数据结构(树状数组,SBT,Splay,线段树等等)去维护每一条链,复杂度为O(logn). 树链剖分是解决在树上进行插点问线,插线问点等一系列树上的问题. 假如现在给你一棵树,然后每两条边之间有一个权值,有一些操作,1:x---y之间的最大权值是多少,2:改变x---y之间的权值 当前这样的操作有很多,如果直接用暴力的方法的话肯定不行,那么就要想一个好的方法,我们可以想一下能不能借助线段树解决,能不能想一种方法对树上的边进行编号,然后就变

[BZOJ2243]SDOI2011染色|树链剖分|LCT

裸题嘛.. 先考虑一条线段上如何查询颜色段数,只要对每个线段树节点多维护一个左颜色和右颜色,然后合并的时候sum[x]=sum[lc]+sum[rc]-(左儿子的右颜色==右儿子的左颜色)..实在太久没写树剖结果码+调试花了两节多晚自习,,各种傻逼错误,什么反向边忘加,标记忘记下传...还有就是更新答案的时候,关键的一点是要保证当前的两点(也就是a,b)是没有被更新到的,否则很难搞.. 表示LCT要更好写..不过在BZOJ上我的树链剖分6000+MS,LCT要13000+MS.. 树链剖分: #

hdu 4897 Little Devil I(树链剖分+线段树)

题目链接:hdu 4897 Little Devil I 题目大意:给定一棵树,每条边有黑白两种颜色,初始都是白色,现在有三种操作: 1 u v:u到v路径上的边都取成相反的颜色 2 u v:u到v路径上相邻的边都取成相反的颜色(相邻即仅有一个节点在路径上) 3 u v:查询u到v路径上有多少个黑色边 解题思路:树链剖分,用两个线段W和L维护,W对应的是每条的黑白情况,L表示的是每个节点的相邻边翻转情况 (对于轻链而言,重链直接在W上修改) 对于1操作,即为普通的树链剖分,直接在W上修改即可.

[BZOJ1036][ZJOI2008]树的统计Count 解题报告|树链剖分

树链剖分 简单来说就是数据结构在树上的应用.常用的为线段树splay等.(可现在splay还不会敲囧) 重链剖分: 将树上的边分成轻链和重链. 重边为每个节点到它子树最大的儿子的边,其余为轻边. 设(u,v)为轻边,则size(v)<=size(u)/2 (一旦大于了那必然是重边) 也就是一条路径上每增加一条轻边节点个数就会减少一半以上,那么显然根到任意一个节点路径上的轻边条数一定不会超过log(n)(不然节点就没了啊23333) 重链定义为一条极长的连续的且全由重边构成的链. 容易看出重链两两