bzoj2588:Count on a tree(可持久化线段树+Lca)

2588: Spoj 10628. Count on a tree

Time Limit: 12Sec  Memory Limit: 128 MB
Submit: 5524  Solved: 1306

Description

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

Input

第一行两个整数N,M。

第二行有N个整数,其中第i个整数表示点i的权值。

后面N-1行每行两个整数(x,y),表示点x到点y有一条边。

最后M行每行两个整数(u,v,k),表示一组询问。

Output

M行,表示每个询问的答案。最后一个询问不输出换行符

Sample Input

8 5

105 2 9 3 8 5 7 7

1 2

1 3

1 4

3 5

3 6

3 7

4 8

2 5 1

0 5 2

10 5 3

11 5 4

110 8 2

Sample Output

2

8

9

105

7

HINT

HINT:

N,M<=100000

题目分析:可持久化线段树,一个节点以它的fa的线段树作为历史版本做适当修改。当我们要查u->v的路径第k小时,我们同时在u,v,lca(u,v),fa[lca(u,v)]四棵树上进行二叉查找。

写代码的时候发现与其将空子树指向NULL还不如指向池子的第0位,然后让第0位指向自身。这样Query的时候就可以在0的地方不停地打转,不会爆栈。

没看清输出格式,PE了一发……

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=100100;
const int maxl=20;
const int oo=1000000001;

struct data
{
	int obj;
	data *Next;
} e[maxn<<1];
data *head[maxn];
int cur=-1;

struct Tnode
{
	int num;
	Tnode *lson,*rson;
} tree[(maxn<<2)+maxn*maxl];
Tnode *Root[maxn];
int Tcur=-1;

struct Input
{
	int Time,val,place;
} a[maxn];
int id[maxn];
int temp=0;

int fa[maxn][maxl];
int dep[maxn];

int n,m;

void Add(int x,int y)
{
	cur++;
	e[cur].obj=y;
	e[cur].Next=head[x];
	head[x]=e+cur;
}

bool Comp1(Input x,Input y)
{
	return x.val<y.val;
}

bool Comp2(Input x,Input y)
{
	return x.Time<y.Time;
}

Tnode *New_node()
{
	Tcur++;
	tree[Tcur].lson=tree[Tcur].rson=tree;
	tree[Tcur].num=0;
	return tree+Tcur;
}

void Update(Tnode *root,int L,int R,int x)
{
	if ( L==x && x==R )
	{
		root->num++;
		return;
	}
	
	int mid=(L+R)>>1;
	if (x<=mid)
	{
		Tnode *y=root->lson;
		root->lson=New_node();
		*(root->lson)=*y;
		Update(root->lson,L,mid,x);
	}
	else
	{
		Tnode *y=root->rson;
		root->rson=New_node();
		*(root->rson)=*y;
		Update(root->rson,mid+1,R,x);
	}
	
	root->num=root->lson->num+root->rson->num;
}

void Dfs(int node)
{
	Root[node]=New_node();
	*Root[node]=*Root[ fa[node][0] ];
	Update(Root[node],1,temp,a[node].place);
	data *p;
	for (p=head[node]; p; p=p->Next)
		if (p->obj!=fa[node][0])
		{
			int son=p->obj;
			fa[son][0]=node;
			dep[son]=dep[node]+1;
			Dfs(son);
		}
}

void Make_fa()
{
	for (int j=1; j<maxl; j++)
		for (int i=1; i<=n; i++)
			fa[i][j]=fa[ fa[i][j-1] ][j-1];
}

int Lca(int u,int v)
{
	if (dep[u]<dep[v]) swap(u,v);
	for (int j=maxl-1; j>=0; j--)
		if ( dep[ fa[u][j] ]>=dep[v] )
			u=fa[u][j];
	if (u==v) return u;
	for (int j=maxl-1; j>=0; j--)
		if (fa[u][j]!=fa[v][j])
		{
			u=fa[u][j];
			v=fa[v][j];
		}
	return fa[u][0];
}

int Query(Tnode *ru,Tnode *rv,Tnode *rw,Tnode *rfw,int L,int R,int k)
{
	if (L==R) return L;
	int left_num=ru->lson->num+rv->lson->num-rw->lson->num-rfw->lson->num;
	int mid=(L+R)>>1;
	if (k<=left_num) return Query(ru->lson,rv->lson,rw->lson,rfw->lson,L,mid,k);
	else return Query(ru->rson,rv->rson,rw->rson,rfw->rson,mid+1,R,k-left_num);
}

int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	
	scanf("%d%d",&n,&m);
	for (int i=1; i<=n; i++) scanf("%d",&a[i].val);
	for (int i=1; i<=n; i++) a[i].Time=i;
	for (int i=1; i<=n; i++) head[i]=NULL;
	for (int i=1; i<n; i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		Add(x,y);
		Add(y,x);
	}
	
	a[0].val=-oo;
	sort(a+1,a+n+1,Comp1);
	for (int i=1; i<=n; i++)
	{
		if (a[i-1].val!=a[i].val) temp++;
		a[i].place=temp;
		id[temp]=a[i].val;
	}
	sort(a+1,a+n+1,Comp2);
	
	//for (int i=1; i<=temp; i++) printf("%d ",id[i]);
	//printf("\n");
	
	Root[0]=New_node();
	fa[1][0]=0;
	dep[1]=1;
	
	Dfs(1);
	
	Make_fa();
	
	/*for (int j=0; j<maxl; j++)
	{
		for (int i=1; i<=n; i++) printf("%d ",fa[i][j]);
		printf("\n");
	}*/
	
	int lastans=0;
	for (int i=1; i<=m; i++)
	{
		int u,v,k;
		scanf("%d%d%d",&u,&v,&k);
		u=u^lastans;
		int w=Lca(u,v);
		//printf("%d\n",w);
		lastans=Query(Root[u],Root[v],Root[w],Root[ fa[w][0] ],1,temp,k);
		lastans=id[lastans];
		printf("%d",lastans);
		if (i!=m) printf("\n");
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值