Count on a tree(树上路径第K小)
题目链接:https://www.spoj.com/problems/COT/en/
题意:求树上A,B两点路径上第K小的数
思路:主席树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表上。
比如说我们从一棵树的根节点进行DFS,得到根节点到各节点的距离dist[x]——这是一个根-x路径上点与根节点距离的前缀和。
利用这个前缀和,我们可以解决一些树上任意路径的问题,比如在线询问[a,b]点对的距离——答案自然是dist[a]+dist[b]-2*dist[lca(a,b)]。
DFS遍历整棵树,然后在每个节点上建立一棵线段树,某一棵线段树的“前一版本”是位于该节点父亲节点fa的线段树。
利用与之前类似的方法插入点权(排序离散)。那么对于询问[a,b],答案就是root[a]+root[b]-root[lca(a,b)]-root[fa[lca(a,b)]]上的第k大。
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<string>
#include<set>
#define ll long long
#define maxn 100007
using namespace std;
const int MAXN=1e5+;
const int POW=;
int num[MAXN],node[MAXN];
struct point
{
int l;
int r;
int sum;
}T[MAXN*];
int root[MAXN];
vector<int> G[MAXN];
int d[MAXN];
int p[MAXN][POW];
int tot;
int f[MAXN];
int n,m;
void build(int l,int r,int& rt)
{
rt=++tot;
T[rt].sum=;
if(l>=r)return;
int m=(l+r)>>;
build(l,m,T[rt].l);
build(m+,r,T[rt].r);
}
void update(int last,int p,int l,int r,int &rt)
{
rt=++tot;
T[rt].l=T[last].l;
T[rt].r=T[last].r;
T[rt].sum=T[last].sum+;
if(l>=r)return ;
int m=(l+r)>>;
if(p<=m)update(T[last].l,p,l,m,T[rt].l);
else update(T[last].r,p,m+,r,T[rt].r);
}
int query(int left_rt,int right_rt,int lca_rt,int lca_frt,int l,int r,int k)
{
if(l>=r)return l;
int m=(l+r)>>;
int cnt=T[T[right_rt].l].sum+T[T[left_rt].l].sum-T[T[lca_rt].l].sum-T[T[lca_frt].l].sum;
if(k<=cnt)
return query(T[left_rt].l,T[right_rt].l,T[lca_rt].l,T[lca_frt].l,l,m,k);
else
return query(T[left_rt].r,T[right_rt].r,T[lca_rt].r,T[lca_frt].r,m+,r,k-cnt);
}
void dfs(int u,int fa,int cnt)
{
f[u]=fa;
d[u]=d[fa]+;
p[u][]=fa;
for(int i=;i<POW;i++)
p[u][i]=p[p[u][i-]][i-];
update(root[fa],num[u],,cnt,root[u]);
for(int i=;i<(int)G[u].size();i++)
{
int v=G[u][i];
if(v==fa)continue;
dfs(v,u,cnt);
}
}
int lca(int a,int b)
{
if(d[a]>d[b])
a^=b,b^=a,a^=b;
if(d[a]<d[b])
{
int del=d[b]-d[a];
for(int i=;i<POW;i++)
if(del&(<<i))b=p[b][i];
}
if(a!=b)
{
for(int i=POW-;i>=;i--)
{
if(p[a][i]!=p[b][i])
{
a=p[a][i],b=p[b][i];
}
}
a=p[a][],b=p[b][];
}
return a;
}
void init()
{
for(int i=;i<=n;i++)
{
G[i].clear();
}
memset(d,,sizeof(d));
memset(p,,sizeof(p));
memset(f,,sizeof(f));
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=;i<=n;i++)
{
scanf("%d",&num[i]);
node[i]=num[i];
}
tot=;
sort(node+,node++n);
int cnt=unique(node+,node+n+)-node-;
for(int i=;i<=n;i++)
{
num[i]=lower_bound(node+,node+cnt+,num[i])-node;
}
int a,b,c;
for(int i=;i<=n-;i++)
{
scanf("%d%d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
build(,cnt,root[]);
dfs(,,cnt );
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
int t=lca(a,b);
int id=query(root[a],root[b],root[t],root[f[t]],,cnt,c);
printf("%d\n",node[id]);
}
}
return ;
}
Count on a tree(树上路径第K小)的更多相关文章
- Count on a tree 树上区间第K小
Count on a tree 题意:求路径 u到v上的 第k小的权重. 题解:先DFS建数, 然后对于每个节点往上跑出一颗主席树, 然后每次更新. 查询的时候, u, v, k, 找到 z = l ...
- SPOJ-COT-Count on a tree(树上路径第K小,可持久化线段树)
题意: 求树上A,B两点路径上第K小的数 分析: 同样是可持久化线段树,只是这一次我们用它来维护树上的信息. 我们之前已经知道,可持久化线段树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表 ...
- Count on a tree 树上主席树
Count on a tree 树上主席树 给\(n\)个树,每个点有点权,每次询问\(u,v\)路径上第\(k\)小点权,强制在线 求解区间静态第\(k\)小即用主席树. 树上主席树类似于区间上主席 ...
- POJ 1741 Tree 求树上路径小于k的点对个数)
POJ 174 ...
- E - Count on a tree 树上第K小
主席树的入门题目,这道题的题意其实就是说,给你一棵树,询问在两个节点之间的路径上的区间第K小 我们如何把树上问题转换为区间问题呢? 其实DFS就可以,我们按照DFS的顺序,对线段树进行建树,那么这个树 ...
- spoj COT - Count on a tree (树上第K小 LCA+主席树)
链接: https://www.spoj.com/problems/COT/en/ 思路: 首先看到求两点之前的第k小很容易想到用主席树去写,但是主席树处理的是线性结构,而这道题要求的是树形结构,我们 ...
- BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]
2588: Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MBSubmit: 5217 Solved: 1233 ...
- BZOJ 2588: Spoj 10628. Count on a tree 树上跑主席树
2588: Spoj 10628. Count on a tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/J ...
- Codeforces 739B Alyona and a tree(树上路径倍增及差分)
题目链接 Alyona and a tree 比较考验我思维的一道好题. 首先,做一遍DFS预处理出$t[i][j]$和$d[i][j]$.$t[i][j]$表示从第$i$个节点到离他第$2^{j}$ ...
随机推荐
- 第八周作业总结&第六次实验报告
实验六 Java异常 实验目的 理解异常的基本概念: 掌握异常处理方法及熟悉常见异常的捕获方法. 实验要求 练习捕获异常.声明异常.抛出异常的方法.熟悉try和catch子句的使用. 掌握自定义异常类 ...
- 第8周课程总结&实验报告6
实验六 Java异常 实验目的 理解异常的基本概念: 掌握异常处理方法及熟悉常见异常的捕获方法. 实验要求 练习捕获异常.声明异常.抛出异常的方法.熟悉try和catch子句的使用. 掌握自定义异常类 ...
- C/C++ 内存模型
C分为四个区:堆,栈,静态全局变量区,常量区 C++内存分为5个区域(堆栈全常代 ): 堆 heap : 由new分配的内存块,其释放编译器不去管,由我们程序自己控制(一个new对应一个delete) ...
- Struts学习(一)
1.Struts开发基础 1.1 MVC的基本概念 mvc将一个应用系统的输入.处理和输出流程按照Model(模型).View(视图)和Controller(控制器)三部分进行分离,划分成模型层.视 ...
- webpack4下url-loader打包图片问题
webpack.condig.js: const path = require('path'); //导入插件 const VueLoaderPlugin = require('vue-loade ...
- 在搭建Maven项目时导入elasticsearch架包时遇到的问题
<!-- 使用elasticsearch 需要导入两个包,从网上复制的可能因为有特殊字符报 cvc-complex-type.2.3: Element 'dependency' cannot h ...
- time和datetime的区别
time在 Python 文档里,time是归类在Generic Operating System Services中,换句话说, 它提供的功能是更加接近于操作系统层面的.通读文档可知,time 模块 ...
- Jmeter添加压力机
名词解释: 主控机:启动Jmeter 的机器 负载机:为压力测试提供除主控机之外资源的机器 步骤: 1.先在其他的负载机(另外的电脑)上启动jmeter-server (jmeter-server.b ...
- java中关键字final,finally,finalize的区别
-final:作为修饰符关键字,有三种用法: 1,如果一个类被声明为final,则意味着它不能被继承. 2,将变量声明为final,则表示它是一个常量,也就是保证它在使用过程中不被修改,被final修 ...
- 实现webpack的实时打包构建
1. 由于每次重新修改代码之后,都需要手动运行webpack打包的命令,比较麻烦,所以使用`webpack-dev-server`来实现代码实时打包编译,当修改代码之后,会自动进行打包构建.2. 运行 ...