BZOJ2588: Spoj 10628. Count on a tree(主席树)
BZOJ2588: Spoj 10628. Count on a tree##
Time Limit: 12 Sec
Memory Limit: 128 MBDescription###
给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。
Input###
第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v,k),表示一组询问。
Output###
M行,表示每个询问的答案。最后一个询问不输出换行符
Sample Input###
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
Sample Output###
2
8
9
105
7
HINT
N,M<=100000
暴力自重。。。
题目地址:BZOJ2588: Spoj 10628. Count on a tree
题目大意: 已经很简洁了
题解:
主席树
先对所有点权离散化一遍(因为要建权值线段树)
然后对树上的每一个节点到根的路径建一棵权值线段树
然后空间为 \(n^2log_{2}n\) 显然 \(MLE\)
因为这些树形态结构都是一样的
而且对于每个非根节点
他所构成的权值线段树只与他父亲差了一个数(他自己的点权)
所以可以按 \(dfs\) 序来建主席树,进行加减运算(主席树特性)
那么x,y两点之间的路径上所构成的权值线段树就可以表示成\[Tree[x]+Tree[y]-Tree[lca(x,y)]-Tree[fa[lca(x,y)] \](画图验证)
具体看代码
AC代码
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
const int N=1e5+5,M=18e5+5;
int n,Q,New,cnt,ans;
int last[N],val[N],hash[N];
int rt[N],ls[M],rs[M],sum[M];
map<int,int> mp;
struct edge{
int to,next;
}e[N<<1];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void add_edge(int u,int v){
e[++cnt]=(edge){v,last[u]};last[u]=cnt;
e[++cnt]=(edge){u,last[v]};last[v]=cnt;
}
int ind,pos[N],sop[N],dep[N],fa[N][18];
void dfs(int u){
pos[u]=++ind;
sop[ind]=u;
for(int i=last[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa[u][0])continue;
fa[v][0]=u;
dep[v]=dep[u]+1;
dfs(v);
}
}
int lca(int a,int b){
if(dep[a]<dep[b])swap(a,b);
for(int i=17;i>=0;i--)
if(dep[fa[a][i]]>=dep[b])
a=fa[a][i];
for(int i=17;i>=0;i--)
if(fa[a][i]!=fa[b][i])
a=fa[a][i],b=fa[b][i];
if(a==b)return a;
return fa[a][0];
}
int sz;
void build(int l,int r,int pre,int &now,int num){
now=++sz;
sum[now]=sum[pre]+1;
if(l==r)return;
ls[now]=ls[pre];
rs[now]=rs[pre];
int mid=(l+r)>>1;
if(num<=mid)build(l,mid,ls[pre],ls[now],num);
else build(mid+1,r,rs[pre],rs[now],num);
}
int solve(int u,int v,int rk){
int a=u,b=v,c=lca(u,v),d=fa[c][0];
a=rt[pos[a]],b=rt[pos[b]],c=rt[pos[c]],d=rt[pos[d]];
int l=1,r=New;
while(l<r){
int mid=(l+r)>>1;
int tmp=sum[ls[a]]+sum[ls[b]]-sum[ls[c]]-sum[ls[d]];
if(tmp>=rk){
r=mid;
a=ls[a],b=ls[b],c=ls[c],d=ls[d];
}else{
l=mid+1;rk-=tmp;
a=rs[a],b=rs[b],c=rs[c],d=rs[d];
}
}
return hash[l];
}
int main(){
n=read();Q=read();
for(int i=1;i<=n;i++)
val[i]=hash[i]=read();
sort(hash+1,hash+n+1);
New=unique(hash+1,hash+n+1)-hash-1;
for(int i=1;i<=New;i++)
mp[hash[i]]=i;
for(int i=1;i<=n;i++)
val[i]=mp[val[i]];
for(int i=1;i<n;i++){
int u=read(),v=read();
add_edge(u,v);
}
dep[1]=1;
dfs(1);
for(int j=1;j<=17;j++)
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
for(int i=1;i<=n;i++)
build(1,New,rt[pos[fa[sop[i]][0]]],rt[i],val[sop[i]]);
while(Q--){
int u=read()^ans,v=read(),rk=read();
ans=solve(u,v,rk);
printf("%d",ans);
if(Q)puts("");
}
return 0;
}
作者:skl_win
出处:https://www.cnblogs.com/shaokele/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。