Count on a tree SPOJ - COT(树上差分+lca+主席树)

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
有挺多题当时做的时候不知道这是啥,在刷某一专题的时候互让发现这个题原来做过,但是丝毫没有印象。。
树上差分+主席树的一道模板题吧。这是树上点差分。在dfs的时候就直接在树上更新进主席树。这道题需要离散化一下。之后就转化成了区间求第k小。代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxx=1e5+100;
struct node{
	int l;
	int r;
	int sum;
}p[maxx*40];
struct edge{
	int to,next;
}e[maxx<<1];
int a[maxx],val[maxx];
int head[maxx<<1],dp[maxx][26],deep[maxx],root[maxx];
int n,m,len,tot,ror;
/*----------事前准备-----------*/
inline void init()
{
	for(int i=0;i<=n;i++)
	for(int j=0;j<=25;j++) dp[i][j]=0;
	memset(head,-1,sizeof(head));
	memset(deep,0,sizeof(deep));
	tot=ror=0;
}
inline void add(int u,int v)
{
	e[tot].to=v,e[tot].next=head[u],head[u]=tot++;
}
/*-------------主席树-------------*/
inline void pushup(int cur)
{
	p[cur].sum=p[p[cur].l].sum+p[p[cur].r].sum;
}
inline int build(int l,int r)
{
	int cur=++ror;
	p[cur].sum=0;
	if(l==r) return cur;
	int mid=l+r>>1;
	p[cur].l=build(l,mid);
	p[cur].r=build(mid+1,r);
	return cur;
}
inline int update(int rot,int l,int r,int pos)
{
	int cur=++ror;
	p[cur]=p[rot];
	p[cur].sum++;
	if(l==r) return cur;
	int mid=l+r>>1;
	if(pos<=mid) p[cur].l=update(p[rot].l,l,mid,pos);
	else p[cur].r=update(p[rot].r,mid+1,r,pos);
	pushup(cur);
	return cur;
}
inline int query(int lrot,int rrot,int frot,int ffrot,int l,int r,int num)
{
	if(l==r) return l;
	int mid=l+r>>1;
	int ret=p[p[lrot].l].sum+p[p[rrot].l].sum-p[p[frot].l].sum-p[p[ffrot].l].sum;
	if(num<=ret) return query(p[lrot].l,p[rrot].l,p[frot].l,p[ffrot].l,l,mid,num);
	else return query(p[lrot].r,p[rrot].r,p[frot].r,p[ffrot].r,mid+1,r,num-ret);
}
/*--------------dfs--------------*/
inline void dfs(int u,int f)
{
	deep[u]=deep[f]+1;
	dp[u][0]=f;
	for(int i=1;i<=25;i++)
	{
		if(dp[u][i-1]) dp[u][i]=dp[dp[u][i-1]][i-1];
		else break;
	}
	root[u]=update(root[f],1,len,lower_bound(val+1,val+1+len,a[u])-val);
	for(int i=head[u];i!=-1;i=e[i].next)
	{
		int to=e[i].to;
		if(to==f) continue;
		dfs(to,u);
	}
}
/*-------------lca-------------*/
inline int get_lca(int x,int y)
{
	if(deep[x]<deep[y]) swap(x,y);
	int tmp=deep[x]-deep[y];
	for(int i=0;i<=25;i++)
	{
		if(tmp&(1<<i)) x=dp[x][i];
	}
	if(x==y) return x;
	for(int i=25;i>=0;i--)
	{
		if(dp[x][i]!=dp[y][i])
		{
			x=dp[x][i];
			y=dp[y][i];
		}
	}
	return dp[x][0];
}
int main()
{
	int x,y,z,pos;
	while(~scanf("%d%d",&n,&m))
	{
		init();
		for(int i=1;i<=n;i++) scanf("%d",&a[i]),val[i]=a[i];
		sort(val+1,val+1+n);
		len=unique(val+1,val+1+n)-val-1;
		root[0]=build(1,len);
		for(int i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			add(x,y);add(y,x);
		}
		deep[0]=0;
		dfs(1,0);
		while(m--)
		{
			scanf("%d%d%d",&x,&y,&z);
			int LCA=get_lca(x,y);
			printf("%d\n",val[query(root[x],root[y],root[LCA],root[dp[LCA][0]],1,len,z)]);
		}
	}
}

努力加油a啊,(o)/~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据引用\[1\]中的描述,p1168问题使用了线段树解法。在构造树的过程中,需要遍历整棵树,所以时间复杂度为O(n)。但是在改变一个元素的值时,时间复杂度只有O(log(n))。求和的时候,树的节点表示一个索引范围内元素值之和,只要将区间分割对应上,平均时间复杂度是O(log(n)),最坏情况下不会超过O(n*log(n))。\[1\] 根据引用\[2\]中的描述,QUERY 5 12应该是第4、5条边的极大值。\[2\] 根据引用\[3\]中的描述,代码中的if(L<=MID)和else if(R>MID)的判断条件是为了确保查询范围在左子树或右子树中。如果加上else,会导致错误。\[3\] #### 引用[.reference_title] - *1* [leetCode307:线段树解法](https://blog.csdn.net/cyd1999/article/details/123963164)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Spoj 375 Qtree 树链剖分 + 线段树 解法](https://blog.csdn.net/niuox/article/details/8145842)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starlet_kiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值