第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. iOS——Xcode中添加第三方库

    一.只有.h和.a文件的库 1.向项目中添加三方库文件 如果添加的第三方库只有.h和.a文件,直接把文件夹拖进项目下面,这时会弹出下面的提示框,一定要勾选下面选择的选项: 这里要注意,在Add to ...

  2. Linux大文件分割split和合并cat使用方法

    本文主要介绍linux下两个命令:split和cat.其中,相信大家都熟悉cat命令,一般用来查看一个文件的内容,但是它还其它的功能,比如这里要介绍的文件合并功能,它可把多个文件内容合并到一个文件中. ...

  3. ORA-12705: Cannot access NLS data files or invalid environment specified

    ASM实例无法启动 [grid@data ~]$ sqlplus / as sysasm SQL*Plus: Release 11.2.0.4.0 Production on Fri Sep 11 0 ...

  4. MyBatis学习笔记(三) 关联关系

    首先给大家推荐几个网页: http://blog.csdn.net/isea533/article/category/2092001 没事看看 - MyBatis工具:www.mybatis.tk h ...

  5. hiho1096_divided_product

    题目 给出两个正整数N和M, N <= 100, M <= 50, 可以将N分解成若干个不相等的正整数A1, A2... Ak的和,且A1, A2 ... Ak的乘积为M的倍数.即 N = ...

  6. visual studio 自带单元测试demo

    0) 创建类,编写方法类1) 在方法点击鼠标右键,在运行测试(T)和调试测试(D)之间会有一个 <创建单元测试>选项.有则进入2,没有则看1.01.0) 菜单栏 工具-->自定义-- ...

  7. Python核心编程-细节

    直接从六张开始看看书里有什么. cmp() len() max() and min() sorted() and reversed() enumerate() and zip() sum() list ...

  8. PacBio下机数据解读

    今天被人问起如何看懂三代的下机数据,虽然解决了别人的问题,但感觉自己还是没有搞透. 基本的目录结构: |-- HG002new_O1l_BP_P6_021315b_MB_100pM | |-- D01 ...

  9. ajax中基本兼容各浏览器的XMLHttpRequest的创建

    function createXHR(){ var xhr = null; if(window.XMLHttpRequest){//判断当前浏览器是否支持XMLHttpRequest xhr = ne ...

  10. 在腾讯云上创建您的SQL Cluster(3)

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