SPOJ 375 QTREE POJ 3237 TREE 树链剖分

http://wenku.baidu.com/view/8861df38376baf1ffc4fada8

先上一篇论文,我总觉得论文说的不够详细,但是可以理解好多东西。

首先,树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护。

通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中size[v]是以v为根的子树的节点个数,全部由重边组成的路径是重路径,根据论文上的证明,任意一点到根的路径上存在不超过logn条轻边和logn条重路径。

这样我们考虑用数据结构来维护重路径上的查询,轻边直接查询。

通常用来维护的数据结构是线段树。

QTREE是给一棵树,有两种操作,一种是修改一条边的权值,另一种是查询两点路径上的最大值。

POJ3237是在上面两个的基础上再加一个取反操作,就是u到v的路径上所有边的权值乘-1。

下面是POJ3237的代码,拿来先当模板用着了。

【YZ的QTREE树链剖分只用了2.6s但是我的要4.5s,怎么会这么慢……】

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define N 100010
#define IDX(l,r) ((l)+(r) | (l)!=(r))
#define INF (1<<30)
int n;
int head[N],cnt;
struct edge
{
	int v,w,next;
}e[N<<1];
int dep[N],sz[N],pnt[N],heavy[N],rev[N],num[N];
int poi;
struct sgt
{
	int mx,mn,lzy;
}tree[N<<1];
int find(int x){ return x==pnt[x]?x:pnt[x]=find(pnt[x]); }
void addedge(int u,int v,int w)
{
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int u,int f)
{
	int v,mx=-1;
	sz[u]=1;
	heavy[u]=-1;
	for(int i=head[u];i!=-1;i=e[i].next)
	if((v=e[i].v)!=f)
	{
		dep[v]=dep[u]+1;
		rev[v]=i^1;
		dfs(v,u);
		sz[u]+=sz[v];
		if(sz[v]>mx)
		{
			mx=sz[v];
			heavy[u]=i;
		}
	}
	if(heavy[u]!=-1) pnt[e[heavy[u]].v]=u;
}
void pushup(int l,int r)
{
	int rt=IDX(l,r),mid=l+r>>1;
	int rtl=IDX(l,mid),rtr=IDX(mid+1,r);
	tree[rt].mx=max(tree[rtl].mx,tree[rtr].mx);
	tree[rt].mn=min(tree[rtl].mn,tree[rtr].mn);
}
void make(int rt)
{
	int tmp=-tree[rt].mx;
	tree[rt].mx=-tree[rt].mn;
	tree[rt].mn=tmp;
}
void pushdown(int l,int r)
{
	int rt=IDX(l,r),mid=l+r>>1;
	int rtl=IDX(l,mid),rtr=IDX(mid+1,r);
	if(tree[rt].lzy)
	{
		tree[rtl].lzy^=1;
		tree[rtr].lzy^=1;
		make(rtl);
		make(rtr);
		tree[rt].lzy=0;
	}
}
void build(int l,int r)
{
	int rt=IDX(l,r);
	tree[rt].mx=-INF;
	tree[rt].mn=INF;
	tree[rt].lzy=0;
	if(l==r) return;
	int mid=l+r>>1;
	build(l,mid);
	build(mid+1,r);
}
void update(int x,int w,int l,int r)
{
	int rt=IDX(l,r);
	if(l==r)
	{
		tree[rt].mx=tree[rt].mn=w;
		return;
	}
	pushdown(l,r);
	int mid=l+r>>1;
	if(x<=mid) update(x,w,l,mid);
	else update(x,w,mid+1,r);
	pushup(l,r);
}
void reverse(int L,int R,int l,int r)
{
	int rt=IDX(l,r);
	if(L<=l && R>=r)
	{
		make(rt);
		tree[rt].lzy^=1;
		return;
	}
	pushdown(l,r);
	int mid=l+r>>1;
	if(L<=mid) reverse(L,R,l,mid);
	if(R>mid) reverse(L,R,mid+1,r);
	pushup(l,r);
}
int query(int L,int R,int l,int r)
{
	int rt=IDX(l,r);
	if(L<=l && R>=r) return tree[rt].mx;
	pushdown(l,r);
	int mid=l+r>>1,ret=-INF;
	if(L<=mid) ret=max(ret,query(L,R,l,mid));
	if(R>mid) ret=max(ret,query(L,R,mid+1,r));
	return ret;
}
int queryuv(int u,int lca)
{
	int ans=-INF;
	while (u!=lca)
	{
		int ed=rev[u];
		if(num[ed]==-1)
		{
			ans=max(ans,e[ed].w);
			u=e[ed].v;
		}
		else
		{
			int p=pnt[u];
			if(dep[p]<dep[lca]) p=lca;
			ans=max(ans,query(num[ed],num[heavy[p]],1,n-1));
			u=p;
		}
	}
	return ans;
}
void init()
{
	build(1,n-1);
	memset(num,-1,sizeof(num));
	dep[1]=poi=0;
	for(int i=1;i<=n;++i) pnt[i]=i;
	dfs(1,0);
	for(int i=1;i<=n;++i)
	if(heavy[i]==-1)
	{
		int pos=i;
		while (pos!=1 && e[heavy[e[rev[pos]].v]].v==pos)
		{
			int t=rev[pos];
			num[t]=num[t^1]=++poi;
			update(num[t],e[t].w,1,n-1);
			pos=e[t].v;
		}
	}
}
void change(int edge,int val)
{
	if(num[edge]==-1) e[edge].w=e[edge^1].w=val;
	else update(num[edge],val,1,n-1);
}
void NGT(int u,int lca)
{
	while (u!=lca)
	{
		int ed=rev[u];
		if(num[ed]==-1)
		{
			e[ed].w=e[ed^1].w=-e[ed].w;
			u=e[ed].v;
		}
		else
		{
			int p=pnt[u];
			if(dep[p]<dep[lca]) p=lca;
			reverse(num[ed],num[heavy[p]],1,n-1);
			u=p;
		}
	}
}
int lca(int u,int v)
{
	while (1)
	{
		int a=find(u),b=find(v);
		if(a==b) return dep[u]<dep[v]?u:v;
		else if(dep[a]>=dep[b]) u=e[rev[a]].v;
		else v=e[rev[b]].v;
	}
}
int solve(int u,int v)
{
	int p=lca(u,v);
	return max(queryuv(u,p),queryuv(v,p));
}
void _negate(int u,int v)
{
	int p=lca(u,v);
	NGT(u,p); NGT(v,p);
}
int main ()
{
	int tt;
	int u,v,w;
	char s[15];
	scanf("%d",&tt);
	while (tt--)
	{
		cnt=0;
		memset(head,-1,sizeof(head));
		scanf("%d",&n);
		for(int i=1;i<n;++i)
		{			
			scanf("%d%d%d",&u,&v,&w);
			addedge(u,v,w);
			addedge(v,u,w);
		}
		init();
		while (scanf("%s",s))
		{
			if(s[0]=='D') break;
			if(s[0]=='Q')
			{
				scanf("%d%d",&u,&v);
				printf("%d\n",solve(u,v));
			}
			else if(s[0]=='C')
			{
				scanf("%d%d",&u,&w);
				change((u-1)<<1,w);
			}
			else
			{
				scanf("%d%d",&u,&v);
				_negate(u,v);
			}
		}
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值