第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. java中在linux下利用jstack检测死锁

    首先,编写一个死锁程序 package deadlock; public class testJstack { final static Object resource_1 = new Object( ...

  2. json遍历key value

    http://blog.csdn.net/lanshengsheng2012/article/details/17679487 public static void main(String[] arg ...

  3. iOS 沙盒(sandbox)机制和文件操作

    本文参看了 http://www.uml.org.cn/mobiledev/201209211.asp#1 这篇文章中的介绍,尊重原著. 1.IOS沙盒机制 IOS应用程序只能在本应用程序中创建的文件 ...

  4. javaScript定义对象的方法

    转自souhu新闻http://news.sohu.com/20110215/n279335637.shtml? javascript定义对象的几种简单方法 1.构造函数方式,全部属性及对象的方法都放 ...

  5. this(C# 参考)

    原文地址:https://msdn.microsoft.com/zh-cn/library/dk1507sz.aspx this 关键字引用类的当前实例,还可用作扩展方法的第一个参数的修饰符. 注意 ...

  6. nodeschool.io 5

    ~~ FILTERED LS ~~ Create a program that prints a list of files in a given directory,filtered by the ...

  7. 《javascript高级程序设计》第六章 Object Creation VS Inheritance

    6.1 理解对象 6.1.1 属性类型 6.1.2 定义多个属性 6.1.3 读取属性的特性6.2 创建对象 6.2.1 工厂模式 6.2.2 构造函数模式 6.2.3 原型模式 6.2.4 组合使用 ...

  8. MySQL 加锁处理分析

    1    背景    1 1.1    MVCC:Snapshot Read vs Current Read    2 1.2    Cluster Index:聚簇索引    3 1.3    2P ...

  9. 数据库中User和Schema的关系

    如果我们想了解数据库中的User和Schema到底什么关系,那么让我们首先来了解一下数据库中User和Schema到底是什么概念.        在SQL Server2000中,由于架构的原因,Us ...

  10. iOS开发几年了,你清楚OC中的这些东西么!!!?

    iOS开发几年了,你清楚OC中的这些东西么!!!? 前言 几年前笔者是使用Objective-C进行iOS开发, 不过在两年前Apple发布swift的时候,就开始了swift的学习, 在swift1 ...