SPOJ 10628 求树上的某条路径上第k小的点
第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号结点到N号结点的路径,使路径上第K+1大的边权尽量小
二分+最短路算法 #include<cstdio> #include<iostream> #include<cstring> #include<algorit ...
- 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. ...
- Ping pong(树状数组求序列中比某个位置上的数小的数字个数)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2492 Ping pong Time Limit: 2000/1000 MS (Java/Others) ...
- Count on a tree(树上路径第K小)
题目链接:https://www.spoj.com/problems/COT/en/ 题意:求树上A,B两点路径上第K小的数 思路:主席树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表上. ...
- SPOJ-COT-Count on a tree(树上路径第K小,可持久化线段树)
题意: 求树上A,B两点路径上第K小的数 分析: 同样是可持久化线段树,只是这一次我们用它来维护树上的信息. 我们之前已经知道,可持久化线段树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表 ...
- 树上第k小,可持久化线段树+倍增lca
给定一颗树,树的每个结点都有权值, 有q个询问,每个询问是 u v k ,表示u到v路径上第k小的权值是多少. 每个结点所表示的线段树,是父亲结点的线段树添加该结点的权值之后形成的新的线段树 c[ro ...
- BZOJ 2588: Spoj 10628. Count on a tree( LCA + 主席树 )
Orz..跑得还挺快的#10 自从会树链剖分后LCA就没写过倍增了... 这道题用可持久化线段树..点x的线段树表示ROOT到x的这条路径上的权值线段树 ----------------------- ...
- Key Vertex (hdu 3313 SPFA+DFS 求起点到终点路径上的割点)
Key Vertex Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Tota ...
- 牛客小白月赛6 C 桃花 dfs 求树上最长直径
链接:https://www.nowcoder.com/acm/contest/136/C来源:牛客网 题目描述 桃花一簇开无主,可爱深红映浅红. ...
随机推荐
- Android 可拖动列表项的ListView
需求分析 一个界面内两个ListView 我关注的栏目列表 上面的要长按后可拖动排序 点击减号后列表项消失 下面列表增加一行 同时存储相应字符串到本地作为标记 未关注栏目列表 普通ListView 点 ...
- 数据库事物四大特性-ACID
事务的:原子性.一致性.分离性.持久性 事物(transaction)是由一些列操作序列构成的执行单元,这些单元要么都做,要么不做,是一个不可分割的工作单元. 数据库事物的四个基本性质(ACID) 1 ...
- 解决Win7中第一次HttpWebRequest连接缓慢、超时等问题
在使用HttpWebRequest的时候发现,第一次连接需要15S左右,我的系统是win7,很崩溃,除了我那个用户可以等待的了,但是第二次,就没有问题了,于是百度之...... http://www. ...
- C语言中的转义字符
转义字符 意义 ASCII码值(十进制) \a 响铃(BEL) 007 \b 退格(BS) ,将当前位置移到前一列 008 \f 换页(FF),将当前位置移到下页开头 012 \n 换行(LF) ,将 ...
- Sql Server中通配符的使用
Sql Server中通配符的使用 通配符_ "_"号表示任意单个字符,该符号只能匹配一个字符."_"可以放在查询条件的任意位置,且只能代表一个字符.一个汉字只 ...
- 创建和使用Windows静态链接库
首先明确这篇文章的目的,我希望大家能够通过这篇文章了解一下如何在实际工作中创建和使用Windows平台下的静态链接库.关于链接库的概念,希望大家参考维基百科"Library"词条( ...
- word-break:break-all和word-wrap:break-word的区别
了解word-break属性 /* 关键字值 */ word-break: normal; word-break: break-all; word-break: keep-all; /* 全局值 */ ...
- python 练习 5
#!/usr/bin/python # -*- coding: utf-8 -*- from collections import deque def z69(): '''猜牌术(1) 魔术师,最上面 ...
- VBA对象模型(1)
关于对象和集合的比喻 Excel的基本单元是Workbook对象:在快餐连锁店中,基本的单元是单个餐馆.使用Excel可以添加工作簿和关闭工作簿,所有打开的工作簿组成了Workbooks集合(Work ...
- 转 数据库中的 date datetime timestamp的区别
转 数据库中的 date datetime timestamp的区别 DATETIME, DATE和TIMESTAMP类型是相关的.本文描述他们的特征,他们是如何类似的而又不同的. DATETIME类 ...