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来源:牛客网 题目描述 桃花一簇开无主,可爱深红映浅红. ...
随机推荐
- 脱壳脚本_手脱壳ASProtect 2.1x SKE -> Alexey Solodovnikov
脱壳ASProtect 2.1x SKE -> Alexey Solodovnikov 用脚本.截图 1:查壳 2:od载入 3:用脚本然后打开脚本文件Aspr2.XX_unpacker_v1. ...
- android程序中界面太大太长太宽如何滚动?
使用ScrollView即可. ScrollView只能容纳一个直接的子控件. 在Android中编写布局一般会用到scrollview嵌套LinearLayout,使页面可以自适应其高度.但是有的机 ...
- python操作mongodb之四cp数据库
from pymongo import MongoClient #连接数据库 client=MongoClient('192.168.30.252',27017) #获取现有数据库的名称 client ...
- 5.4.2 使用配置启动firefox
1.使用firefox的本地配置加载浏览器 使用本地配置加载浏览器,代码如下. 这样运行后,可以看到firebug等插件都已启动. 练习:假设做性能测试时,需要获取某个状况下的页面网络运行参数.要求完 ...
- python 练习 18
#!/usr/bin/python # -*- coding: UTF-8 -*- import time print time.strftime('%Y-%m-%d %H:%M:%S',time.l ...
- [saiku] 集成单点登录
思路: 自定义一个loginCallbackFilter用于单点登录成功后执行模拟用户认证授权登录的操作. 当授权成功后所有配置需要授权才能访问的url就再也不会被任何filter拦截,可随意访问了. ...
- [转]用Python读写Excel文件
[转]用Python读写Excel文件 转自:http://www.gocalf.com/blog/python-read-write-excel.html#xlrd-xlwt 虽然天天跟数据打交 ...
- android 模拟器 使用键盘的配置
1 打开 android Manageer , 克隆一个设备 2.
- Valid Palindrome [LeetCode]
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignori ...
- 史上最用心的 iOS App 上架流程
题记 麻痹起来嗨!看网上那么多的教程,依然在我心爱的爱屁屁在上架的时候遇到各种 J8 问题,最大的问题就是:Xcode 证书什么的,Provisioning Profile 什么的,Debug 什么的 ...