SPOJ 10628 Count on a tree (lca+主席树)

题意:给定一棵有n个结点的树,每一个点有一个权值。共同拥有m个询问。对于每一个询问(u,v,k),回答结点u至v之间第k小的点的权值。

思路:主席树+lca。首先指定一个根结点dfs一次并在此过程中建好主席树。对于对于每一个询问,我们仅仅须要考虑四棵树,即T[u], T[v], T[lca(u,v)], 再加上T[fa( lca(u,v) )],fa( lca(u,v) )表示lca(u, v)的父亲结点。

这样一来问题就和线性序列里第k小的数一样了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii (pair<int, int>)
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

//const int maxn = 100000 + 100;
//const int INF = 0x3f3f3f3f;

const int maxn = 100000+10000;
const int M = 2000000;
int n, q, m, tot;
int t[maxn], w[maxn], fa[maxn];
int T[maxn], lson[M], rson[M], c[M];

struct Quest {
	int l, r, k;
} quest[maxn];

void Init_hash(int k) {
	sort(t, t+k);
	m = unique(t, t+k) - t;
}

int Hash(int x) {
	return lower_bound(t, t+m, x) - t;
}

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 Insert(int root, int pos, int val) {
	int newroot = tot++, tmp = newroot;
	int l = 0, r = m-1;
	c[newroot] = c[root] + val;
    while(l < r) {
        int mid = (l+r)>>1;
        if(pos <= mid) {
            lson[newroot] = tot++; rson[newroot] = rson[root];
            newroot = lson[newroot]; root = lson[root];
            r = mid;
        }
        else {
            rson[newroot] = tot++; lson[newroot] = lson[root];
            newroot = rson[newroot]; root = rson[root];
            l = mid+1;
        }
        c[newroot] = c[root] + val;
    }
	return tmp;
}

int Query(int l_root, int r_root, int lca, int k) {
	int l = 0, r = m -1, lca_root = T[lca], fa_root = fa[lca];
	while(l < r) {
		int mid = (l+r) >> 1;
		int tmp = c[lson[l_root]]+c[lson[r_root]]-c[lson[lca_root]]-c[lson[fa_root]];
		if(tmp >= k) {
			r = mid;
			l_root = lson[l_root]; r_root = lson[r_root]; lca_root = lson[lca_root]; fa_root = lson[fa_root];
		}
		else {
			l = mid + 1;
			k -= tmp;
			l_root = rson[l_root]; r_root = rson[r_root]; lca_root = rson[lca_root]; fa_root = rson[fa_root];
		}
	}
	return l;
}

int pnt[maxn], lca[maxn];
bool vis[maxn];
vector<int> G[maxn], query[maxn], num[maxn];
int find(int x) {
	if(x == pnt[x]) return x;
	return pnt[x] = find(pnt[x]);
}
void dfs_lca(int u) {
	vis[u] = 1; pnt[u] = u;
	int sz1 = G[u].size();
	for(int i = 0; i < sz1; i++) {
		int v = G[u][i];
		if(vis[v]) continue;
		fa[v] = T[u];
		dfs_lca(v);
		pnt[v] = u;
	}
	int sz2 = query[u].size();
	for(int i = 0; i < sz2; i++) {
		int v = query[u][i];
		if(vis[v]) lca[num[u][i]] = find(v);
	}
}
void init() {
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; i++) {
		G[i].clear();
		query[i].clear();
		num[i].clear();
	}
}

void dfs_ZXTree(int cur, int fa) {
	int sz = G[cur].size();
	for(int i = 0; i < sz; i++) {
		int u = G[cur][i];
		if(u == fa) continue;
		T[u] = Insert(T[cur], Hash(w[u]), 1);
		dfs_ZXTree(u, cur);
	}
}

int main() {
    //freopen("input.txt", "r", stdin);
    while(cin >> n >> q) {
		init();
		m = 0; tot = 0;
		for(int i = 1; i <= n; i++) scanf("%d", &w[i]), t[m++] = w[i];
		Init_hash(m);
		build(0, m-1);
		for(int i = 1; i < n; i++) {
			int u, v; scanf("%d%d", &u, &v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		T[1] = Insert(T[0], Hash(w[1]), 1);
		dfs_ZXTree(1, -1);
		for(int i = 0; i < q; i++) {
			int l, r, k; scanf("%d%d%d", &l, &r, &k);
			quest[i].l = l; quest[i].r = r; quest[i].k = k;
			query[l].push_back(r); query[r].push_back(l);
			num[l].push_back(i); num[r].push_back(i);
		}
		fa[1] = T[0];
		dfs_lca(1);
		for(int i = 0; i < q; i++) {
			printf("%d\n", t[Query(T[quest[i].l], T[quest[i].r], lca[i], quest[i].k)]);
		}
    }
    return 0;
}

??

??

时间: 2024-10-13 02:20:08

SPOJ 10628 Count on a tree (lca+主席树)的相关文章

bzoj 2588: Spoj 10628. Count on a tree LCA+主席树

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一行两个整数N,M. 第二行有N个整数,其中第i个整数

BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 5217  Solved: 1233[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一

SPOJ COT Count on a tree(主席树+倍增lca)

思路:这个题其实就是树上的第k小,主席树的本质还是类似于前缀和一样的结构,所以是完全相同的,所以我们在树上也可以用同样的方法,我们对于每一个节点进行建树,然后和普通的树上相同,ab之间的距离是等于 root[a]+root[b]-root[lca[a,b]]-root[fa[lca[a,b]]] 代码: 复制代码#include <bits/stdc++.h>using namespace std;const int maxn=1e5+7;const int POW=18;int num[ma

BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca

2588: Spoj 10628. Count on a tree 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),表示一组询问.

【BZOJ2588】Spoj 10628. Count on a tree 主席树+LCA

[BZOJ2588]Spoj 10628. Count on a tree 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),表示一组

BZOJ 2588: Spoj 10628. Count on a tree 树上跑主席树

2588: Spoj 10628. Count on a tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2588 Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Inp

SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

COT - Count on a tree #tree 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 

【主席树】bzoj2588 Spoj 10628. Count on a tree

每个点的主席树的root是从其父转移来的.询问的时候用U+V-LCA-FA(LCA)即可. #include<cstdio> #include<algorithm> using namespace std; #define N 100001 int v[N<<1],first[N],next[N<<1],en,Ans; void AddEdge(int U,int V) { v[++en]=V; next[en]=first[U]; first[U]=en;

BZOJ2588 Spoj 10628. Count on a tree

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! 题目链接:BZOJ2588 SPOJ10628 正解:主席树上树 解题报告: 考虑主席树上树,每次build都是儿子在父亲的基础上build,然后查询的话就是对于x到y的路径,令$Tx$为$x$对应的线段树,那就是$T_x+T_y-T_{lca}-T_{f