SPOJ10628--COT(树上第K大)主席树

You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight.

We will ask you to perform the following operation:

  • u v k : ask for the kth minimum weight on the path from node u to node v

 

Input

In the first line there are two integers N and M.(N,M<=100000)

In the second line there are N integers.The ith integer denotes the weight of the ith node.

In the next N-1 lines,each line contains two integers u v,which describes an edge (u,v).

In the next M lines,each line contains three integers u v k,which means an operation asking for the kth minimum weight on the path from node u to node v.

Output

For each operation,print its result.

Example

Input:

 
 
8 5
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 2 5 2 2 5 3 2 5 4 7 8 2 
Output:
2
8
9
105
7 
题意:求u到v路径上的第K大值
思路:和普通的区间第K大类似,不过普通第K大是每棵线段树根据后一棵线段树建立,而树上的主席树是每个节点根据父亲节点建立。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 208000
#define maxm 2500000
int T[maxn],first[100080],nxt[maxn],vv[maxn],dfn[maxn],dep[maxn],key[maxn],X[maxn];
int c[maxm],lson[maxm],rson[maxm];
int tot,e,n,m,fuck,cnt;
int fa[maxn][20];//fa[i][j]表示i的2^j祖先是谁。
void scanf_(int & num)
{
	char in;
	while((in=getchar())>'9'||in<'0');  
	num=in-'0';
	while(in=getchar(),in>='0'&&in<='9') 
		 num*=10,num+=in-'0'; 
}
void addedge(int u,int v)
{
	vv[e] = v;	nxt[e] = first[u];	first[u] = e++;
	vv[e] = u;	nxt[e] = first[v];	first[v] = e++;
}

int build(int l,int r)
{
	int root = tot++;
	c[root] = 0;
	if(l != r)
	{
		int mid = (l+r) >> 1;
		lson[root] = build(l,mid);
		rson[root] = build(mid+1,r);
	}
	return root;
}

int Update(int root,int pos,int val)
{
	int newnode = tot++,tmp = newnode;
	c[newnode] = c[root] + val;
	int l = 1,r = fuck;
	while(l < r)
	{
		int mid = (l+r) >> 1;
		if(pos <= mid)
		{
			r = mid;
			lson[newnode] = tot++;		rson[newnode] = rson[root];
			newnode = lson[newnode];	root = lson[root];
		}
		else 
		{
			l = mid + 1;
			rson[newnode] = tot++;		lson[newnode] = lson[root];
			newnode = rson[newnode];	root = rson[root];
		}
		c[newnode] = c[root] + val;
	}
	return tmp;
}

int Query(int u,int v,int lca,int lca_root,int k)
{
	int l = 1,r = fuck;
	int pos = lower_bound(X+1,X+fuck,key[lca]) - X;
	while(l < r)
	{
		int mid = (l+r) >> 1;
		if(c[lson[u]] + c[lson[v]] - 2*c[lson[lca_root]] + (pos >= l && pos <= mid) >= k)
		{
			r = mid;
			u = lson[u];
			v = lson[v];
			lca_root = lson[lca_root];
		}
		else 
		{
			
			k -= c[lson[u]] + c[lson[v]] - 2*c[lson[lca_root]] + (pos >= l && pos <= mid);
			u = rson[u];
			v = rson[v];
			lca_root = rson[lca_root];
			l = mid + 1;
		}
	}
	return l;
}

int LCA(int u,int v)
{
	if(dfn[u] == dfn[v])
		return u;
	if(dfn[u] < dfn[v])
		swap(u,v);
	//if(dfn[fa[u][0]] <= dfn[v])
		//return LCA(fa[u][0],v);
	for(int i = 1;i >= 0;i++)
	{
		if(dfn[fa[u][i]] < dfn[v])
			return LCA(fa[u][i-1],v);
		if(dfn[fa[u][i]] == dfn[v])
			return v;
	}
}


void init()
{
	memset(first,-1,sizeof(first));
	memset(dfn,0,sizeof(dfn));
	dfn[1] = dep[1] = 0;
	tot = e = cnt = 0;
}

void dfs(int u,int pre)//建树同时求出每个点的2的若干次幂祖先
{
	int pos = lower_bound(X+1,X+1+fuck,key[u]) - X;
	T[u] = Update(T[pre],pos,1);
	dfn[u] = ++cnt;
	dep[u] = dep[pre] + 1;
	fa[u][0] = pre;
	for(int j = 1;dep[u] - (1<<j) >= 0;j++)
		fa[u][j] = fa[fa[u][j-1]][j-1];
	for(int i = first[u];i != -1;i = nxt[i])
	{
		int v = vv[i];
		if(v == pre)	continue;
		dfs(v,u);
	}
}

int main()
{
	//freopen("in.txt","r",stdin);
	memset(fa,0,sizeof(fa));
	scanf_(n);scanf_(m);  
	init();
	for(int i = 1;i <= n;i++)
	{
		scanf_(key[i]);
		X[i] = key[i];
	}
	sort(X+1,X+1+n);
	fuck = unique(X+1,X+1+n) - X - 1;
	T[0] = build(1,fuck);
	for(int i = 1;i < n;i++)
	{
		int u,v;
		scanf_(u),scanf_(v);
		addedge(u,v);
	}
	dfs(1,0);
	while(m--)
	{
		int u,v,k;
		scanf_(u),scanf_(v),scanf_(k);
		int lca = LCA(u,v);
		printf("%d\n",X[Query(T[u],T[v],lca,T[lca],k)]);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值