给定一颗树,树的每个结点都有权值,

有q个询问,每个询问是 u v k ,表示u到v路径上第k小的权值是多少。

每个结点所表示的线段树,是父亲结点的线段树添加该结点的权值之后形成的新的线段树

c[root] 表示根为root的子树添加了多少个结点。

那么c[lson[u]] + c[lson[v]] - c[lson[lca(u,v)]] - c[lson[fa[lca(u,v)]]]  >=k ,那么说明左子树添加了k个以上的结点,说明第k小的值在左子树

否则就在右子树。

 //
// main.cpp
// 函数式线段树
//
// Created by whoami on 15/9/21.
// Copyright (c) 2015年 whoami. All rights reserved.
// #include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
using namespace std;
const int N = + ;
const int M = ;
int t[N],lson[N],rson[N],c[N],total;
int a[M],b[M];
int n,m,q;
int head[M],nxt[M],to[M],e;
int dfs_clock,iid[M];
int fa[M][],depth[M];
void addEdge(int u, int v){
to[e] = v;
nxt[e] = head[u];
head[u] = e++;
} //离散化,离散化后有多少个点,线段树的区间就是多大
void initHash(){
sort(b+,b+n+);
m = unique(b+,b+n+) - b - ;
}
int hs(int x){
return lower_bound(b+,b+m+,x)-b;
} int build(int l, int r){
int root = total++;
c[root] = ;
if(l!=r){
int mid = (l+r)>>;
lson[root] = build(l,mid);
rson[root] = build(mid+,r);
}
return root;
}
int update(int root, int pos, int val){
int newRoot = total++,tmp = newRoot;
c[newRoot] = c[root] + val;
int l =, r = m;
while(l<r){
int mid = (l+r)>>;
if(pos<=mid){
r = mid;
lson[newRoot] = total++;
rson[newRoot] = rson[root];
newRoot = lson[newRoot];
root = lson[root];
}
else{
l = mid + ;
lson[newRoot] = lson[root];
rson[newRoot] = total++;
newRoot = rson[newRoot];
root = rson[root];
}
c[newRoot] = c[root] + val;
}
return tmp;
}
void dfs(int u, int f, int dep){
fa[u][] = f;
depth[u] = dep; for(int i=head[u]; i+;i=nxt[i]){
int v = to[i];
if(v==f)continue;
t[++dfs_clock] = update(iid[u],hs(a[v]),);
iid[v] = t[dfs_clock];
dfs(v,u,dep+);
}
} int query(int urt, int vrt, int lcart, int frt, int k){
int l = , r = m;
//当l==r,即区间的长度只有1时,那么该区间所对应的值就是第k小了
while(l<r){
int mid = (l+r)>>;
if(c[lson[urt]] + c[lson[vrt]] - c[lson[frt]]-c[lson[lcart]]>=k){
r = mid;
urt = lson[urt];
vrt = lson[vrt];
frt = lson[frt];
lcart = lson[lcart];
}
else
{
l = mid+;
k -= c[lson[urt]] + c[lson[vrt]] - c[lson[frt]]-c[lson[lcart]];
urt = rson[urt];
vrt = rson[vrt];
frt = rson[frt];
lcart = rson[lcart];
}
}
return l;
}
void init() {
for(int k=;k+<; ++k){
for(int v = ;v<=n;++v){
if(fa[v][k]<)
fa[v][k+] = -;
else
fa[v][k+] = fa[fa[v][k]][k];
}
}
} int lca(int u, int v){
if(depth[u] < depth[v])
swap(u,v); int tmp = depth[u] - depth[v];
for(int i=;i>=;--i)
if(tmp &(<<i))
u = fa[u][i];
if(u==v) return u;
for(int i=;i>=;--i){
if(fa[u][i]!=fa[v][i]){
u = fa[u][i];
v = fa[v][i];
}
}
return fa[u][]; }
int main(int argc, const char * argv[]) {
int u,v,k;
while(scanf("%d%d",&n,&q)!=EOF){
total = dfs_clock = ;
for(int i=;i<=n;++i){
scanf("%d",&a[i]);
b[i] = a[i];
}
memset(head,-,sizeof(head));
for(int i=;i<n;++i){
scanf("%d%d",&u,&v);
addEdge(u,v);
addEdge(v,u);
}
addEdge(,);
addEdge(,);
initHash();
iid[] = t[] = build(,m);
memset(fa,-,sizeof(fa));
dfs(,,); init();
while(q--){
scanf("%d%d%d",&u,&v,&k);
int lc = lca(u,v);
int f = fa[lc][];
printf("%d\n",b[query(iid[u],iid[v],iid[lc],iid[f],k)]);
}
} return ;
}

树上第k小,可持久化线段树+倍增lca的更多相关文章

  1. POJ- 2104 hdu 2665 (区间第k小 可持久化线段树)

    可持久化线段树 也叫函数式线段树也叫主席树,其主要思想是充分利用历史信息,共用空间 http://blog.sina.com.cn/s/blog_4a0c4e5d0101c8fr.html 这个博客总 ...

  2. 区间第K小——可持久化线段树模板

    概念 可持久化线段树又叫主席树,之所以叫主席树是因为这东西是fotile主席创建出来的. 可持久化数据结构思想,就是保留整个操作的历史,即,对一个线段树进行操作之后,保留访问操作前的线段树的能力. 最 ...

  3. 序列内第k小查询(线段树)

    最近请教了一下大佬怎么求序列内第k大查询,自己又捣鼓了一下,虽然还没有懂得区间第k大查询,不过姑且做一个记录先吧 因为每个元素大小可能很大而元素之间不连续,所以我们先离散化处理一下,程序中的ori[ ...

  4. HDU 2665.Kth number-可持久化线段树(无修改区间第K小)模板 (POJ 2104.K-th Number 、洛谷 P3834 【模板】可持久化线段树 1(主席树)只是输入格式不一样,其他几乎都一样的)

    Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  5. SPOJ-COT-Count on a tree(树上路径第K小,可持久化线段树)

    题意: 求树上A,B两点路径上第K小的数 分析: 同样是可持久化线段树,只是这一次我们用它来维护树上的信息. 我们之前已经知道,可持久化线段树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表 ...

  6. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  7. 【可持久化线段树】POJ2104 查询区间第k小值

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 61284   Accepted: 21504 Ca ...

  8. [TS-A1505] [清橙2013中国国家集训队第二次作业] 树 [可持久化线段树,求树上路径第k大]

    按Dfs序逐个插入点,建立可持久化线段树,每次查询即可,具体详见代码. 不知道为什么,代码慢的要死,, #include <iostream> #include <algorithm ...

  9. BZOJ 2588: Spoj 10628. Count on a tree-可持久化线段树+LCA(点权)(树上的操作) 无语(为什么我的LCA的板子不对)

    2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 9280  Solved: 2421 ...

随机推荐

  1. NTP工作机制及时间同步的方法

    Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,它能够使计算机对其server或时钟源做同步化,它能够提供高精准度的时间校正,且可用加密确认的方式来防止恶毒的协 ...

  2. CorePlot学习

    阅读这篇文章,指出它在国外    原文地址:https://github.com/core-plot/core-plot/wiki/High-Level-Design-Overview 强烈推荐阅读该 ...

  3. Cocos2d-x 创建(create)动画对象CCAnimation报错分析

    本人在使用精灵表单创建动画的过程中突然遇到了一些个问题,下面进行一下分析总结. 根据在Cocos2d-iphone中的经验,我写出了如下的代码: CCSpriteFrameCache::sharedS ...

  4. UVA 1386 - Cellular Automaton(循环矩阵)

    UVA 1386 - Cellular Automaton option=com_onlinejudge&Itemid=8&page=show_problem&category ...

  5. Delphi中编写无输出函数名的DLL文件(有点意思)(400多篇博客)

    用 Delphi 用长了,总是发现,有些和 MS 不同的地方.例如,MS 的公开库中,常常隐藏了许多重要函数,这些函数在系统中常常有起着非常巨大的作用.一旦知道如何调用,可以给自己的应用程序提供很强的 ...

  6. dokcer 运行和进入容器

    <pre name="code" class="html">docker:/root# docker run -itd --name zjtest8 ...

  7. java学习笔记10--枚举

    java学习笔记10--枚举 在JDK1.5之前,java可以有两种方式定义新类型:类和接口.对于大部分面向对 象编程来说,这两种方法看起来似乎足够了,但是在一些特殊情况下,这些方法就不适合.例如,想 ...

  8. How-To: add EPEL repository to Centos 6.x is Easy!

    How-To: add EPEL repository to Centos 6.x is Easy! | ITek Blog How-To: add EPEL repository to Centos ...

  9. iOS 如何创建单例对象

    一.什么是单例? 说到单例我就想起了我的java啊 ,不禁感叹起我的大学时光,学了4年的java开发,到现在还是放弃了我的java,踏入了iOS的行列. 算了,入正轨,我现在正是铁树银花的青春美少女, ...

  10. E. Mike and Foam(容斥原理)

    E. Mike and Foam Mike is a bartender at Rico's bar. At Rico's, they put beer glasses in a special sh ...