题目描述

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

输入

第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。

输出

M行,表示每个询问的答案。

样例输入

8 5
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5 1
0 5 2
10 5 3
11 5 4
110 8 2

样例输出

2
8
9
105
7


题解

主席树+最近公共祖先

需要明确主席树的原理:线段树相加减。

那么A到B的路径就是 A到根的路径+B到根的路径-最近公共祖先到根的路径-最近公共祖先的父亲到根的路径。

可以直接在树上建立主席树,注意每棵树是从它父亲的树推来的。

然后查询即可。

注意最后一行千万不能有换行,否则无限PE!

#include <cstdio>
#include <algorithm>
#define N 100001
using namespace std;
struct data
{
int num , rank;
}a[N];
int root[N] , lp[N << 5] , rp[N << 5] , sum[N << 5] , val[N] , top , tot;
int head[N] , to[N << 1] , next[N << 1] , cnt , fa[N] , bl[N] , deep[N] , si[N] , q[N] , tail;
bool cmp1(data a , data b)
{
return a.num < b.num;
}
bool cmp2(data a , data b)
{
return a.rank < b.rank;
}
void add(int x , int y)
{
to[++cnt] = y;
next[cnt] = head[x];
head[x] = cnt;
}
void dfs1(int x)
{
int i;
si[x] = 1;
for(i = head[x] ; i ; i = next[i])
{
if(to[i] != fa[x])
{
fa[to[i]] = x;
deep[to[i]] = deep[x] + 1;
dfs1(to[i]);
si[x] += si[to[i]];
}
}
}
void dfs2(int x , int c)
{
int i , k = 0;
bl[x] = c;
q[++tail] = x;
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa[x] && si[to[i]] > si[k])
k = to[i];
if(k)
{
dfs2(k , c);
for(i = head[x] ; i ; i = next[i])
if(to[i] != fa[x] && to[i] != k)
dfs2(to[i] , to[i]);
}
}
int getlca(int x , int y)
{
while(bl[x] != bl[y])
{
if(deep[bl[x]] < deep[bl[y]])
swap(x , y);
x = fa[bl[x]];
}
if(deep[x] < deep[y]) return x;
return y;
}
void pushup(int x)
{
sum[x] = sum[lp[x]] + sum[rp[x]];
}
void ins(int x , int &y , int l , int r , int p)
{
y = ++tot;
if(l == r)
{
sum[y] = sum[x] + 1;
return;
}
int mid = (l + r) >> 1;
if(p <= mid) rp[y] = rp[x] , ins(lp[x] , lp[y] , l , mid , p);
else lp[y] = lp[x] , ins(rp[x] , rp[y] , mid + 1 , r , p);
pushup(y);
}
int query(int a , int b , int c , int d , int l , int r , int p)
{
if(l == r) return val[l];
int mid = (l + r) >> 1;
if(sum[lp[a]] + sum[lp[b]] - sum[lp[c]] - sum[lp[d]] >= p) return query(lp[a] , lp[b] , lp[c] , lp[d] , l , mid , p);
else return query(rp[a] , rp[b] , rp[c] , rp[d] , mid + 1 , r , p - sum[lp[a]] - sum[lp[b]] + sum[lp[c]] + sum[lp[d]]);
}
int main()
{
int n , m , i , x , y , z , f , last = 0;
scanf("%d%d" , &n , &m);
for(i = 1 ; i <= n ; i ++ )
{
scanf("%d" , &a[i].num);
a[i].rank = i;
}
sort(a + 1 , a + n + 1 , cmp1);
val[0] = 0x80000000;
for(i = 1 ; i <= n ; i ++ )
{
if(a[i].num != val[top]) val[++top] = a[i].num;
a[i].num = top;
}
sort(a + 1 , a + n + 1 , cmp2);
for(i = 1 ; i < n ; i ++ )
{
scanf("%d%d" , &x , &y);
add(x , y);
add(y , x);
}
dfs1(1);
dfs2(1 , 1);
for(i = 1 ; i <= tail ; i ++ )
ins(root[fa[q[i]]] , root[q[i]] , 1 , top , a[q[i]].num);
while(m -- )
{
scanf("%d%d%d" , &x , &y , &z);
x ^= last;
f = getlca(x , y);
last = query(root[x] , root[y] , root[f] , root[fa[f]] , 1 , top , z);
printf("%d" , last);
if(m) printf("\n");
}
return 0;
}

【bzoj2588】Spoj 10628. Count on a tree 离散化+主席树的更多相关文章

  1. 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 ...

  2. BZOJ 2588: Spoj 10628. Count on a tree( LCA + 主席树 )

    Orz..跑得还挺快的#10 自从会树链剖分后LCA就没写过倍增了... 这道题用可持久化线段树..点x的线段树表示ROOT到x的这条路径上的权值线段树 ----------------------- ...

  3. bzoj 2588 Spoj 10628. Count on a tree(主席树)

    Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始 ...

  4. bzoj 2588: Spoj 10628. Count on a tree【主席树+倍增】

    算是板子,把值离散化,每个点到跟上做主席树,然后查询的时候主席树上用u+v-lca-fa[lca]的值二分 #include<iostream> #include<cstdio> ...

  5. bzoj2588: Spoj 10628. Count on a tree(树上第k大)(主席树)

    每个节点继承父节点的树,则答案为query(root[x]+root[y]-root[lca(x,y)]-root[fa[lca(x,y)]]) #include<iostream> #i ...

  6. BZOJ2588: Spoj 10628. Count on a tree

    传送门 刚开始看错题以为是dfs序瞎搞.. 后来看清题了开始想用树剖瞎搞... 感觉要滚粗啊.. 对于每个点到根的路径建立线段树,暴力建MLE没跑,上主席树,然后$(x,y)$的路径就可以先求出来$L ...

  7. 【主席树】bzoj2588 Spoj 10628. Count on a tree

    每个点的主席树的root是从其父转移来的.询问的时候用U+V-LCA-FA(LCA)即可. #include<cstdio> #include<algorithm> using ...

  8. 主席树初探--BZOJ2588: Spoj 10628. Count on a tree

    n<=100000的点权树,有m<=100000个询问,每次问两个点间的第k小点权,保证有解,强制在线. 主席上树啦!类似于之前的序列不带修改询问的前缀表示法,现在只要把前缀当成某点到根的 ...

  9. 【bzoj2588/P2633】count on a tree —— LCA + 主席树

    (以下是luogu题面) 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问 ...

随机推荐

  1. 重庆Uber优步司机奖励政策

    获得任何奖励的前提条件:当周接单率80%以上,当周乘客评分4.5分以上,且无刷单等欺诈行为. 滴滴快车单单2.5倍,注册地址:http://www.udache.com/如何注册Uber司机(全国版最 ...

  2. Spark入门(Python版)

    Hadoop是对大数据集进行分布式计算的标准工具,这也是为什么当你穿过机场时能看到”大数据(Big Data)”广告的原因.它已经成为大数据的操作系统,提供了包括工具和技巧在内的丰富生态系统,允许使用 ...

  3. MySQL不能连接本地数据库10061

    可能的原因是本地服务器没有启动,在安装配置MySQL时,我去掉了开机自动开启,所以开机之后出现了错误10061 解决办法: 一.计算机右击选择管理 二.选择服务,找到MySQL,右击手动,选择启动服务

  4. CentOS 7.2使用tomcat部署jenkins2.130

    一.jenkins介绍 Jenkins是一个功能强大的应用程序,允许持续集成和持续交付项目,无论用的是什么平台.这是一个免费的源代码,可以处理任何类型的构建或持续集成.集成Jenkins可以用于一些测 ...

  5. Unity Android设备的输入

    Unity Android设备的输入 1依据屏幕位置输入 有的时候也许是为了整个有些风格的干净,减少屏幕上的UI图标,以至于摒弃了虚拟按键这种常用的输入方式.为了替代虚拟按键的输入方式而选择了依据点击 ...

  6. CodeForces - 776C(前缀和+思维)

    链接:CodeForces - 776C 题意:给出数组 a[n] ,问有多少个区间和等于 k^x(x >= 0). 题解:求前缀和,标记每个和的个数.对每一个数都遍历到1e5,记录到答案. # ...

  7. 204. Singleton

    Description Singleton is a most widely used design pattern. If a class has and only has one instance ...

  8. node事件循环

    Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.j ...

  9. ArcFaceDemo 第二版【C#】——视频人脸识别

    啥话不说,不用跪求,直接给下载地址:http://common.tenzont.com/comdll/arcface2demo.zip(话说附件的大小不限制,还是说我的文件太大,实际上确实有点大,60 ...

  10. kaldi HMM-GMM全部训练脚本分解

    目录 train_mono.sh train_deltas.sh train_lda_mllt.sh train_sat.sh train_mono.sh 单音素训练脚本: //初始化,[topo f ...