码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ 3123 SDOI2013 森林 可持久化线段树+倍增LCA+启发式合并

时间:2014-10-20 13:36:39      阅读:291      评论:0      收藏:0      [点我收藏+]

标签:bzoj   bzoj3123   可持久化线段树   倍增lca   启发式合并   

题目大意:给定一棵森林,每个点有权值,提供两种操作:

1.查询两点间路径上第k小的权值

2.将两个点之间连一条边 保证连接后仍是一座森林

可持久化线段树部分同Count On A Tree 只是这道题加了个连接操作

对于连接操作我们要用到启发式合并 就是把小的那棵树暴力重建 很简单的一个操作但是可以证明是均摊O(nlogn)的

大小我用了并查集 其实记录根就可以了

此外本题的多组数据是在逗比 记住testcase恒等于1就是了

NND我倍增LCA又写错了0.0 预处理时居然从大往小写的0.0 样例还过了0.0

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 80100
using namespace std;
struct Tree{
	Tree *ls,*rs;
	int num;
}*tree[M],mempool[20002000],*C=mempool;
struct abcd{
	int to,next;
}table[M<<1];
int head[M],tot;
int n,m,q,ans;
int a[M],fa[M][20],dpt[M],root[M],siz[M],stack[M],top;
int Find(int x)
{
	if(!root[x])
		siz[x]=1,root[x]=x;
	if(root[x]==x)
		return x;
	return root[x]=Find(root[x]);
}
inline void Unite(int x,int y)
{
	int fx=Find(x);
	int fy=Find(y);
	root[fy]=fx;
	siz[fx]+=siz[fy];
}
inline void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
inline Tree* New_Node(Tree* _,Tree* __,int ___)
{
	C->ls=_;
	C->rs=__;
	C->num=___;
	return C++;
}
Tree* Build_Tree(Tree *p,int x,int y,int num)
{
	int mid=x+y>>1;
	if(x==y)
		return New_Node(0x0,0x0,p->num+1);
	if(num<=mid)
		return New_Node(Build_Tree(p->ls,x,mid,num),p->rs,p->num+1);
	else
		return New_Node(p->ls,Build_Tree(p->rs,mid+1,y,num),p->num+1);
}
int Get_Kth(Tree *p1,Tree *p2,Tree *p3,Tree *p4,int x,int y,int k)
{
	int mid=x+y>>1;
	if(x==y)
		return mid;
	int temp = p1->ls->num + p2->ls->num - p3->ls->num - p4->ls->num;
	if(k<=temp)
		return Get_Kth(p1->ls,p2->ls,p3->ls,p4->ls,x,mid,k);
	else
		return Get_Kth(p1->rs,p2->rs,p3->rs,p4->rs,mid+1,y,k-temp);
}
void DFS1(int x)
{
	int i;
	dpt[x]=dpt[fa[x][0]]+1;
	tree[x]=Build_Tree(tree[fa[x][0]],0,1000000000,a[x]);
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==fa[x][0])
			continue;
		fa[table[i].to][0]=x;
		DFS1(table[i].to);
	}
}
void DFS2(int x,int from)
{
	int i;
	fa[x][0]=from;
	dpt[x]=dpt[fa[x][0]]+1;
	tree[x]=Build_Tree(tree[fa[x][0]],0,1000000000,a[x]);
	stack[++top]=x;
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==from)
			continue;
		DFS2(table[i].to,x);
	}
}
inline int LCA(int x,int y)
{
	int j;
	if(dpt[x]<dpt[y])
		swap(x,y);
	for(j=19;~j;j--)
		if(dpt[fa[x][j]]>=dpt[y])
			x=fa[x][j];
	if(x==y)
		return x;
	for(j=19;~j;j--)
		if(fa[x][j]!=fa[y][j])
			x=fa[x][j],y=fa[y][j];
	return fa[x][0];
}
int main()
{
	int T,i,j,x,y,k;
	char p[10];
	cin>>T;
	cin>>n>>m>>q;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=m;i++)
		scanf("%d%d",&x,&y),Add(x,y),Add(y,x),Unite(x,y);
	tree[0]=New_Node(C,C,0);
	for(i=1;i<=n;i++)
		if(!fa[i][0])
			DFS1(i);
	for(j=1;j<=19;j++)
		for(i=1;i<=n;i++)
			fa[i][j]=fa[ fa[i][j-1] ][j-1];
	for(i=1;i<=q;i++)
	{
		scanf("%s%d%d",p,&x,&y);
		x^=ans;y^=ans;
		if(p[0]=='Q')
		{
			scanf("%d",&k);k^=ans;
			int lca=LCA(x,y);
			printf("%d\n", ans=Get_Kth(tree[x],tree[y],tree[lca],tree[fa[lca][0]],0,1000000000,k) );
		}
		else
		{
			if(siz[Find(x)]>siz[Find(y)])
				swap(x,y);
			top=0;
			DFS2(x,y);
			Unite(x,y);
			Add(x,y),Add(y,x);
			for(j=1;j<=19;j++)
				for(k=1;k<=top;k++)
					fa[ stack[k] ][j]=fa[ fa[ stack[k] ][j-1] ][j-1];
		}
	}
	
}


BZOJ 3123 SDOI2013 森林 可持久化线段树+倍增LCA+启发式合并

标签:bzoj   bzoj3123   可持久化线段树   倍增lca   启发式合并   

原文地址:http://blog.csdn.net/popoqqq/article/details/40298469

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!