zoukankan      html  css  js  c++  java
  • SPOJ-COT Count on a tree(树上的可持久化线段树)

    题意:你被给予了N个节点的树。树的节点编号从1到N。每个节点都有权值。会有如下的操作:
    u v k:询问从路径u到路径v的第k小的权值

    分析:对于可持久化线段树来说,每一棵线段树都维护着序列的前缀。那么转换到树上,就是维护从根节点到当前节点的前缀,因此,对于路径u,v来说,路径u,v上的第k小的权值是root[u] + root[v] -root[lca(u, v)] - root[pa[lca(u, v)]]。root[u] + root[v]让lca(u, v)多加了两次,要减去一次-root[lca(u, v)]。
    如下图所示:

    root[u]表示从根到u的前缀,root[v]表示从根到v的前缀,root[lca(u, v)]表示从根到lca(u, v)的前缀。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 100005;
    
    int n, m;
    
    int w[N];
    vector<int> nums;
    
    struct ST
    {
    	int lc, rc;
    	int cnt;
    }tr[4 * N + 20 * N];
    
    int fa[N][20];
    int dep[N];
    
    int root[N], tot;
    int h[N], e[N * 2], ne[N * 2], idx;
    
    void add(int a, int b)
    {
    	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    void pushup(int u)
    {
    	tr[u].cnt = tr[tr[u].lc].cnt + tr[tr[u].rc].cnt;
    }
    
    int insert(int p, int l, int r, int x)
    {
    	int q = ++tot;
    	tr[q] = tr[p];
    	if (l == r)
    	{
    		++tr[q].cnt;
    		return q;
    	}
    	int mid = l + r >> 1;
    	if (x <= mid) tr[q].lc = insert(tr[p].lc, l, mid, x);
    	else tr[q].rc = insert(tr[p].rc, mid + 1, r, x);
    	tr[q].cnt = tr[tr[q].lc].cnt + tr[tr[q].rc].cnt;
    	return q;
    }
    
    //pp:最近公共祖先的父节点
    int query(int u, int v, int p, int pp, int l, int r, int k)
    {
    	if (l == r) return l;
    	int mid = l + r >> 1;
    	int num = tr[tr[u].lc].cnt + tr[tr[v].lc].cnt - tr[tr[p].lc].cnt - tr[tr[pp].lc].cnt;
    	if (k <= num) return query(tr[u].lc, tr[v].lc, tr[p].lc, tr[pp].lc, l, mid, k);
    	else return query(tr[u].rc, tr[v].rc, tr[p].rc, tr[pp].rc, mid + 1, r, k - num);
    }
    
    
    void dfs(int u, int father)
    {
    	fa[u][0] = father;
    	root[u] = insert(root[fa[u][0]], 0, nums.size() - 1, w[u]);
    	dep[u] = dep[father] + 1;
    	for (int i = 1; i <= 19; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    	for (int i = h[u]; i != -1; i = ne[i])
    	{
    		int j = e[i];
    		if (j == father) continue;
    		dfs(j, u);
    	}
    }
    
    int lca(int a, int b)
    {
    	if (dep[a] < dep[b]) swap(a, b);
    	for (int k = 19; k >= 0; --k)
    	{
    		if (dep[fa[a][k]] >= dep[b])
    		{
    			a = fa[a][k];
    		}
    	}
    	if (a == b) return a;
    	for (int k = 19; k >= 0; --k)
    	{
    		if (fa[a][k] != fa[b][k])
    		{
    			a = fa[a][k];
    			b = fa[b][k];
    		}
    	}
    	return fa[a][0];
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    
    	for (int i = 1; i <= n; ++i)
    	{
    		scanf("%d", &w[i]);
    		nums.push_back(w[i]);//离散化值域
    	}
    
    	sort(nums.begin(), nums.end());
    	nums.erase(unique(nums.begin(), nums.end()), nums.end());
    
    	for (int i = 1; i <= n; ++i)
    	{
    		w[i] = lower_bound(nums.begin(), nums.end(), w[i]) - nums.begin();//存储离散化的位置
    	}
    	int u, v;
    	memset(h, -1, sizeof h);
    
    	for (int i = 1; i < n; ++i)
    	{
    		scanf("%d%d", &u, &v);
    		add(u, v), add(v, u);
    	}
    
    	dfs(1, 0);//插入
    
    	int k;
    	while (m--)
    	{
    		scanf("%d%d%d", &u, &v, &k);
    		int p = lca(u, v);
    		printf("%d
    ", nums[query(root[u], root[v], root[p], root[fa[p][0]], 0, nums.size() - 1, k)]);
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    博客园
    未释放的已删除文件
    ssh连接缓慢
    剑指 Offer 38. 字符串的排列
    剑指 Offer 37. 序列化二叉树
    剑指 Offer 50. 第一个只出现一次的字符
    剑指 Offer 36. 二叉搜索树与双向链表
    剑指 Offer 35. 复杂链表的复制
    剑指 Offer 34. 二叉树中和为某一值的路径
    剑指 Offer 33. 二叉搜索树的后序遍历序列
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13904634.html
Copyright © 2011-2022 走看看