spoj 10628 Count on a tree

本文介绍了一种解决树上查询问题的方法:利用函数式线段树。通过离散化处理和DFS序列建立函数式线段树,文章详细讲解了如何高效地查询路径上第k小的值,并分享了实现过程中的心得与技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//	spoj 10628 Count on a tree
//
//	题目大意:
//		一棵树上,节点有权值.给q个查询,询问u到v路径上第k小的值
//
//	解题思路:
//
//		函数式线段树.首先离散化,将最后不同的元素的个数设为tot记录下来,然后
//	先建立一颗1-tot的空的线段树.按照dfs序列建立函数式线段树.问什么按照dfs序列.
//	因为这样可以将树上节点变成线性结构.我们需要的答案只与四颗树有关即rt[u],
//	rt[v],rt[LCA(u,v)],rt[father[LCA(u,v)]].函数式线段树也满足加减性质.所以十分
//	方便.
//
//	感悟:
//		
//		这道题搞了好久,是看着大神的代码做的.主要是学习一下函数式线段树.然而我敲出来
//	就是wa.开始瞎bb弄了个LCA-倍增算法的模板一套,一直wa.我还没怀疑LCA错了.最后只差LCA
//	了,我才发现LCA真的错了...然后怒用树链剖分的方式求LCA,结果过了~~~心情还是很激动的呢
//	感觉函数式线段树就是每次对于前面一颗线段树建立一个新的节点保存之前没有修改的节点
//	的信息,而且更省空间.主要作用应该是计数问题,继续加油吧~~~FIGHTING!!!

#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#define For(x,a,b,c) for (int x = a; x <= b; x += c)
#define Ffor(x,a,b,c) for (int x = a; x >= b; x -= c)
#define cls(x,a) memset(x,a,sizeof(x))
using namespace std;
typedef long long ll;

const double PI = acos(-1.0);
const double eps = 1e-9;
const int MAX_N = 1e5 + 10000;

const int INF = 1e9 + 7;
const ll MOD = 1e9 + 7;

int N,M;
int a[MAX_N];
int b[MAX_N];
vector<int> g[MAX_N];

//int pa[MAX_N][20];
//int dep[MAX_N];


int top[MAX_N];
int idx[MAX_N];
int siz[MAX_N];
int son[MAX_N];
int dep[MAX_N];
int father[MAX_N];

struct node{
	int ls;
	int rs;
	int cnt;
};
int tot;
struct IntervalTree{
	node p[MAX_N * 20];
	int rt[MAX_N];
	int Siz;

	void init(){
		Siz = 1;
	}

	int build(int L,int R){
		int rt = Siz++;
		p[rt].cnt = 0;
		if (L == R){
			return rt;
		}
		int M = (L + R) >> 1;

		p[rt].ls = build(L,M);
		p[rt].rs = build(M+1,R);
		return rt;
	}
	
	int update(int rt,int L,int R,int q,int v){
		int nrt = Siz++;
		p[nrt] = p[rt];
		p[nrt].cnt += v;

		if (L == R){
			return nrt;
		}
		
		int M = (L + R) >> 1;

		if (q <= M)
			p[nrt].ls = update(p[rt].ls,L,M,q,v);
		else 
			p[nrt].rs = update(p[rt].rs,M+1,R,q,v);
		return nrt;
	}

	int query(int rtl,int rtr,int rlca,int rf_lca,int L,int R,int k){
		if (L == R){
			return L;
		}

		int M = (L + R) >> 1;
		int tmp = p[p[rtl].ls].cnt + p[p[rtr].ls].cnt - p[p[rlca].ls].cnt - p[p[rf_lca].ls].cnt;
		if (tmp >= k){
			return query(p[rtl].ls,p[rtr].ls,p[rlca].ls,p[rf_lca].ls,L,M,k);
		}else 
			return query(p[rtl].rs,p[rtr].rs,p[rlca].rs,p[rf_lca].rs,M+1,R,k - tmp);
	}

}it;

void dfs(int u,int fa,int d){
	dep[u] = d;
	father[u] = fa;
	son[u] = 0;
	siz[u] = 1;
	//pa[u][0] = fa;
	For(i,0,g[u].size()-1,1){
		int v = g[u][i];
		if (v == fa)
			continue;
		dfs(v,u,d+1);
		siz[u] += siz[v];
		if (siz[son[u]] < siz[v])
			son[u] = v;
	}
}

void dfs_2(int u,int tp){
	top[u] = tp;
	//idx[u] = id++;
	if (son[u])
		dfs_2(son[u],tp);
	For(i,0,g[u].size()-1,1){
		int v =g[u][i];
		if (v == father[u] || v == son[u])
			continue;
		dfs_2(v,v);
	}
}

int LCA_init(){
	dfs(1,0,1);
	dfs_2(1,1);
}

int LCA(int u,int v){
	int p = top[u];
	int q = top[v];
	while(p != q){
		if (dep[p] < dep[q]){
			swap(p,q);
			swap(u,v);
		}
		u = father[p];
		p = top[u];
	}
	if (dep[u] > dep[v])
		swap(u,v);
	return u;
}

//void LCA_init(){
//	cls(pa,-1);
//	dfs(1,1,1);
//	for (int j = 1;(1<<j) <= N; j++){
//		for (int i = 1;i <= N ;i ++){
//				pa[i][j] = pa[pa[i][j-1]][j-1];
//		}
//	}
//}
//
//int LCA(int u,int v){
//	int i,j;
//
//	if (dep[u] < dep[v])
//		swap(u,v);
//	for (i = 0;(1<<i) <= dep[u];i++);
//
//	i--;
//
//	for (j = i; j>= 0;j --)
//		if (dep[u] - (1<<j) >= dep[v])
//			u = pa[u][j];
//
//	if (u == v)
//		return u;
//
//	for (j = i; j >=0;j --){
//		if (pa[u][j]!=-1 && pa[u][j]!=pa[v][j]){
//			u = pa[u][j];
//			v = pa[v][j];
//		}
//	}
//	return pa[u][0];
//}

void dfs(int u,int fa){
	For(i,0,g[u].size()-1,1){
		int v =g[u][i];
		if (v == fa)
			continue;
		it.rt[v] = it.update(it.rt[u],1,tot,a[v],1);
		//it.rt[v] = it.update(it.rt[u],a[v],1);
		dfs(v,u);
	}
}
void print(){
	For(i,1,N,1)
		printf("%d ",a[i]);
	cout << endl;
}
void input(){
	int m = 0;
	For(i,1,N,1){
		scanf("%d",&a[i]);
		b[i] = a[i];
	}

	For(i,1,N-1,1){
		int u,v;
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}

	sort(b+1,b+N+1);

	tot = unique(b+1,b+N+1) - b - 1;
//	b[tot + 1] = INF;
	
	//cout << " ---" << tot << endl;	
	it.init();
	it.rt[0] = it.build(1,tot);

	For(i,1,N,1){
		a[i] = lower_bound(b,b+tot+1,a[i]) - b;
	}

	//print();

	it.rt[1] = it.update(it.rt[0],1,tot,a[1],1);
	
	//it.rt[1] = it.update(it.rt[0],a[1],1);

	dfs(1,-1);

	LCA_init();
}


void solve(){
	For(i,1,M,1){
		int u,v,k;
		scanf("%d%d%d",&u,&v,&k);
		int t = LCA(u,v);
		//printf("%d %d %d %d\n",it.rt[u],it.rt[v],t,k);
		printf("%d\n",b[it.query(it.rt[u],it.rt[v],it.rt[t],it.rt[father[t]],1,tot,k)]);
		//printf("%d\n",b[it.query(it.rt[u],it.rt[v],t,k)]);
	}
}


void init(){
	For(i,1,N,1){
		g[i].clear();
	}
	cls(dep,0);
}

int main(){
	//freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	while(scanf("%d%d",&N,&M)!=EOF){
		init();
		input();
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值