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

有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. CF 258B Little Elephant and Elections [dp+组合]

    给出1,2,3...m 任取7个互不同样的数a1,a2,a3,a4,a5,a6,a7 一个数的幸运度是数位上4或7的个数 比方244.470幸运度是2. 44434,7276727.4747,7474 ...

  2. TApplication.Initialize的前世今生

    ---------------------------------------------------------------------------------------------------- ...

  3. 爱的歌我uhegierhiuerh5怕哦一

    http://www.huihui.cn/share/8424421 http://www.huihui.cn/share/8424375 http://www.huihui.cn/share/842 ...

  4. c++/c/java 资源共享群

    Hi,我邀请你加入360云盘文件共享群:c++&c&java, 打开邀请链接:http://qun.yunpan.360.cn/10005202 , 输入邀请码:1357

  5. 在ListView中实现排序

    此处介绍的情境是: (1)使用table布局ListView. (2)ListView的数据源是List<T>. (3)排序字段2个(帖子的回复次数和浏览次数),都是int类型. 基本思路 ...

  6. [Java][Android][Process] ProcessBuilder与Runtime差别

    在Android中想要进行Ping,在不Root机器的情况下似乎还仅仅能进行底层命调用才干实现. 由于在Java中要进行ICMP包发送须要Root权限. 于是仅仅能通过创建进程来攻克了.创建进程在Ja ...

  7. 用HTTP方式调用gearman任务处理

    本来以为是个挺美好的东西,结果... 这样的方式非常不安全,尤其是假设暴露在公网地址,非常easy被攻击,并且gearman的http服务远没有专业的webserver健壮. 攻击方式非常easy:t ...

  8. [Android阅读代码]圆形旋转菜单CircleMenu

    项目名称:圆形旋转菜单CircleMenu 原版项目代码下载 感谢原作者开源

  9. 【Android】读取sdcard卡上的全部图片而且显示,读取的过程有进度条显示

    尽管以下的app还没有做到快图浏览.ES文件浏览器的水平,遇到大sdcard还是会存在读取过久.内存溢出等问题,可是基本思想是这种. 例如以下图.在sdcard卡上有4张图片, 打开app,则会吧sd ...

  10. 【ALearning】第二章 Androidproject知识介绍

    本章介绍了主要的初步Androidproject成立了一个开发环境.为了Android意识的整体项目和理解.本章包含Android开发环境的搭建.第一Android工程Hello World与Andr ...