BZOJ2588: Spoj 10628. Count on a tree(主席树)


BZOJ2588: Spoj 10628. Count on a tree##

  Time Limit: 12 Sec
  Memory Limit: 128 MB

Description###

   给定一棵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/
  本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

posted @ 2018-08-21 21:37  skl_win  阅读(285)  评论(0编辑  收藏  举报
Live2D