第k小,很容易会想到用主席树来解决

这里简单想一下树的转移过程

因为本身无向图形成一棵树,那么我们总以1为根,那么之后连下去的边对应的点建立的线段树总是在父亲节点对应的树上加上一个当前点对应位置出现的值

这跟在普通序列上由前一个转移到下一个是差不多的

那么每个点上生成的线段树记录的就是当前节点到根节点的总信息

然后每次询问求出2个点的公共祖先,那么找第k小,总是两个点的总前缀 减去一个 公共祖先的前缀和公共祖先父亲的前缀

那么询问的时候只要查询这四个点对应的线段树的值就可以了

 #include <bits/stdc++.h>

 using namespace std;
#define N 100010
#define lowbit(x) x&(-x)
#define define_m int m=(l+r)>>1
#define LS(x) node[x].ls
#define RS(x) node[x].rs
#define INIT(x) node[x].init()
#define SZ(x) node[x].sz int a[N] , val[N] , fa[N] , T[N];
int n , m , first[N] , k , curn;//curn表示离散化后还剩多少种数 //LCA所需
int dp[N<<][];
int id[N<<] , dep[N<<] , No[N] , dfs_clock; int HASH(int x){return lower_bound(a+ , a+curn+ , x)-a;} struct Node{
int ls , rs , sz;
void init(){ls=rs=sz=;}
}node[N*]; int tot;
int build(int l , int r)
{
int u=tot++;
INIT(u);
if(l!=r){
define_m;
LS(u) = build(l , m);
RS(u) = build(m+ , r);
}
return u;
} void build(int o1 , int o2 , int l , int r , int pos , int v)
{
if(l==r){
SZ(o2) = SZ(o1)+v;
return;
}
define_m;
INIT(tot);
if(m>=pos){
LS(o2) = tot++ , RS(o2) = RS(o1);
build(LS(o1) , LS(o2) , l , m , pos , v);
}else{
LS(o2) = LS(o1) , RS(o2) = tot++;
build(RS(o1) , RS(o2) , m+ , r , pos , v);
}
SZ(o2) = SZ(LS(o2)) + SZ(RS(o2));
} int query(int u , int v , int anc1 , int anc2 , int l , int r , int k)
{
if(l==r) return l;
define_m;
int c = SZ(LS(u))+SZ(LS(v))-SZ(LS(anc1))-SZ(LS(anc2));
if(c>=k) return query(LS(u) , LS(v) , LS(anc1) , LS(anc2) , l , m , k);
else return query(RS(u) , RS(v) , RS(anc1) , RS(anc2) , m+ , r , k-c);
} struct Edge{
int x , y , next;
Edge(){}
Edge(int x , int y , int next):x(x),y(y),next(next){}
}e[N<<]; void add_edge(int x , int y){
e[k] = Edge(x , y , first[x]);
first[x] = k++;
} void dfs(int u , int f , int d)
{
fa[u] = f , id[++dfs_clock] = u , No[u] = dfs_clock , dep[dfs_clock] = d;
INIT(tot);
T[u] = tot++;
build(T[f] , T[u] , , curn , HASH(val[u]) , );
for(int i=first[u] ; ~i ; i=e[i].next){
int v = e[i].y;
if(v == f) continue;
dfs(v , u ,d+);
id[++dfs_clock] = u , dep[dfs_clock] = d;
}
} void ST(int n){
for(int i= ; i<=n ; i++) dp[i][] = i;
for(int j= ; (<<j)<=n ; j++){
for(int i= ; i+(<<j)-<=n ; i++){
int a = dp[i][j-] , b=dp[i+(<<(j-))][j-];
dp[i][j] = dep[a]<dep[b]?a:b;
}
}
} int RMQ(int l , int r){
int k= ;
while((<<(k+))<=r-l+) k++;
int a = dp[l][k] , b=dp[r-(<<k)+][k];
return dep[a]<dep[b]?a:b;
} int LCA(int u , int v)
{
int x = No[u] , y = No[v];
if(x>y) swap(x , y);
return id[RMQ(x,y)];
} int main()
{
// freopen("in.txt" , "r" , stdin);
//freopen("out1.txt" , "w" , stdout);
while(~scanf("%d%d" , &n , &m)){
k = ;
memset(first , - , sizeof(first));
for(int i= ; i<=n ; i++){scanf("%d" , &val[i]);a[i]=val[i];}
sort(a+ , a+n+);
curn = unique(a+ , a+n+)-(a+);
// cout<<"check: "<<curn<<endl;
for(int i= ; i<n ; i++){
int x , y;
scanf("%d%d" , &x , &y);
add_edge(x , y);
add_edge(y , x);
}
dfs_clock = ;
tot=;
INIT(tot);
T[] = build( , curn);
dfs(,,);
ST(*n-);
while(m--){
int x , y , k;
scanf("%d%d%d" , &x , &y , &k);
int anc = LCA(x , y);
int index = query(T[x] , T[y] , T[anc] , T[fa[anc]] , , curn , k);
printf("%d\n" , a[index]);
}
}
return ;
}

SPOJ 10628 求树上的某条路径上第k小的点的更多相关文章

  1. 在加权无向图上求出一条从1号结点到N号结点的路径,使路径上第K+1大的边权尽量小

    二分+最短路算法 #include<cstdio> #include<iostream> #include<cstring> #include<algorit ...

  2. binary-tree-maximum-path-sum——二叉树任意一条路径上的最大值

    Given a binary tree, find the maximum path sum. The path may start and end at any node in the tree. ...

  3. Ping pong(树状数组求序列中比某个位置上的数小的数字个数)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2492 Ping pong Time Limit: 2000/1000 MS (Java/Others) ...

  4. Count on a tree(树上路径第K小)

    题目链接:https://www.spoj.com/problems/COT/en/ 题意:求树上A,B两点路径上第K小的数 思路:主席树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表上. ...

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

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

  6. 树上第k小,可持久化线段树+倍增lca

    给定一颗树,树的每个结点都有权值, 有q个询问,每个询问是 u v k ,表示u到v路径上第k小的权值是多少. 每个结点所表示的线段树,是父亲结点的线段树添加该结点的权值之后形成的新的线段树 c[ro ...

  7. BZOJ 2588: Spoj 10628. Count on a tree( LCA + 主席树 )

    Orz..跑得还挺快的#10 自从会树链剖分后LCA就没写过倍增了... 这道题用可持久化线段树..点x的线段树表示ROOT到x的这条路径上的权值线段树 ----------------------- ...

  8. Key Vertex (hdu 3313 SPFA+DFS 求起点到终点路径上的割点)

    Key Vertex Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Tota ...

  9. 牛客小白月赛6 C 桃花 dfs 求树上最长直径

    链接:https://www.nowcoder.com/acm/contest/136/C来源:牛客网 题目描述 桃花一簇开无主,可爱深红映浅红.                            ...

随机推荐

  1. 转:strcpy实现的考察要点

    strcpy函数的实现 已知strcpy函数的原型是: char *strcpy(char *dst, const char *src); 实现strcpy函数 解释为什么要返回char * 假如考虑 ...

  2. aliyun CentOS6.5 上 svn 安装笔记

    为了方便管理自己的一些学习资料.总结等,在CentOS6.5上安装SVN,记录过程如下: 1.1  安装方式  独立服务器  fsfs SVN服务器有2种运行方式:独立安装.SVN+Apache.独立 ...

  3. hiho1122_二分图匈牙利算法

    题目 给定一个图的N个节点和节点之间的M条边,数据保证该图可以构成一个二分图.求该二分图最大匹配. 题目链接:二分图最大匹配     首先通过染色法,将图的N个节点分成两个部分:然后通过匈牙利算法求二 ...

  4. hiho_1048_状态压缩2

    题目大意 用1x2的单元拼接出 NxM的矩形,单元可以横放或者纵放,N < 1000, M <= 5. 求不同的拼接方案总数. 分析 计算机解决问题的基本思路:搜索状态空间.如果采用dfs ...

  5. jdk1.6安装

    下载jdk1.6 进入oracle官网,依次选择 Downloads-->Java SE-->拉倒网页最底部,点击Previous Releases - Java Archive后的DOE ...

  6. 让页面滑动流畅得飞起的新特性:Passive Event Listeners

    版权声明:本文由陈志兴原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/153 来源:腾云阁 https://www.qclo ...

  7. SAP MM事务代码清单

  8. 【转】 Linux下目录结构

    装完Linux,首先需要弄清Linux 标准目录结构 / root — 启动Linux时使用的一些核心文件.如操作系统内核.引导程序Grub等. home — 存储普通用户的个人文件 ftp — 用户 ...

  9. Qt之自定义控件(开关按钮)

    简述 接触过IOS系统的童鞋们应该对开关按钮很熟悉,在设置里面经常遇到,切换时候的滑动效果比较帅气. 通常说的开关按钮,有两个状态:on.off. 下面,我们利用自定义控件来实现一个开关按钮. 简述 ...

  10. (30)odoo中的快捷标签

    * 快捷标签   提供快捷标签是为了简化代码的编码,把复杂的工作封装化   * 找到封装化的源码:  openerp/tools/convert.py   xml_import      self._ ...