SPOJ 10628 Count on a tree【主席树+LCA】

C o u n t   o n   a   t r e e Count\ on\ a\ tree Count on a tree

https://vjudge.net/problem/SPOJ-COT
Description:
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
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

TJ:

其实就是在树上的主席树,普通的主席树查询区间[L,R]第K大的时候建树是按区间[0,i]的权值建树,而树上的主席树则按根节点到当前点的权值建树,当查询节点u~v之间的第k大时,和普通主席树的求法差不多,还是用类似前缀和的方法来做,因为每棵树都是记录根节点到当前节点的权值,所以减去重复计算的时候要用到LCA,记u和v的LCA为lca
则u到v区间的权值和表示为:
sum = node[u].sum+node[v].sum-node[lca].sum-node[fa[lca]].sum
然后和普通主席树一样做就好了

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int maxn = 1e5+7;
vector<int> vec;
int getid(int x){ return lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1; }
int n,m,seq[maxn],root[maxn],fa[maxn][19],dep[maxn],head[maxn],edge_num,maxdep;
struct EDGE{
	int to,next;
}edge[maxn<<2];
struct ljt_Tree{
	struct Node{
		int l,r,sum;
	}node[maxn<<5];
	int cnt;
	ljt_Tree(){cnt=0;}
	void update(int l,int r,int &now,int pre,int pos){
		node[++cnt] = node[pre];
		node[now=cnt].sum++;
		if(l+1==r) return;
		int mid = (l+r)>>1;
		if(pos<mid) update(l,mid,node[now].l,node[pre].l,pos);
		else update(mid,r,node[now].r,node[pre].r,pos);
	}
	int query(int l,int r,int x,int y,int lca,int flca,int k){
		if(l+1==r) return l;
		int sum = node[node[x].l].sum+node[node[y].l].sum-node[node[lca].l].sum-node[node[flca].l].sum;
		int mid = (l+r)>>1;
		if(sum>=k) return query(l,mid,node[x].l,node[y].l,node[lca].l,node[flca].l,k);
		else return query(mid,r,node[x].r,node[y].r,node[lca].r,node[flca].r,k-sum);
	}
}Seg_Tree;
void add_edge(int u,int v){
	edge[edge_num].to = v;
	edge[edge_num].next = head[u];
	head[u] = edge_num++;
}
void dfs(int now,int pre,int depth){
	maxdep = max(maxdep,depth);
	dep[now] = depth;
	if(now) Seg_Tree.update(1,n+1,root[now],root[pre],getid(seq[now]));
	fa[now][0] = pre;
	for(int i=1;i<=18;i++) fa[now][i] = fa[fa[now][i-1]][i-1];
	for(int i=head[now];i!=-1;i=edge[i].next){
		int v = edge[i].to;
		if(v==pre) continue;
		dfs(v,now,depth+1);
	}
}
int LCA(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=0;dep[u]!=dep[v];i++) if((dep[u]-dep[v])>>i & 1) u = fa[u][i];
	if(u==v) return u;
	for(int i=log2(maxdep);i>=0;i--){
		if(fa[u][i]!=fa[v][i]){
			u = fa[u][i];
			v = fa[v][i];
		}
	}
	return fa[u][0];
}
int main(){
	scanf("%d %d",&n,&m);
	memset(head,255,sizeof(head));
	for(int i=1;i<=n;i++) scanf("%d",&seq[i]),vec.push_back(seq[i]);
	sort(vec.begin(),vec.end());
	vec.erase(unique(vec.begin(),vec.end()),vec.end());
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		add_edge(u,v),add_edge(v,u);
	}
	add_edge(0,1),add_edge(1,0);
	dfs(0,0,0);
	for(int i=1;i<=m;i++){
		int u,v,k;
		scanf("%d %d %d",&u,&v,&k);
		int lca = LCA(u,v);
		printf("%d\n",vec[Seg_Tree.query(1,n+1,root[v],root[u],root[lca],root[fa[lca][0]],k)-1]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值