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

2588: Spoj 10628. Count on a tree

Time Limit: 12 Sec   Memory Limit: 128 MB
Submit: 3980   Solved: 932
[ Submit][ Status][ Discuss]

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




HINT:

N,M<=100000

暴力自重。。。

Source

[ Submit][ Status][ Discuss]
题解:树上主席树

大体思路就是对树上的每个点权进行离散化,然后对树上的的每一个节点到根的路径建一棵权值线段树,因为这些树形态结构都是相似的所有可以进行加减运算(主席树特性)。那么x,y两点之间的路径就可以表示成root[x]+root[y]-root[lca(x,y)]-root[fa[lca(x,y)],然后进行求解即可。

遇到的问题:直接对每一个节点建立的权值线段树,没有按照DFS序建树,于是RE了。为什么呢?因为在建树时是动态开点,也就是在他父亲所在的树上新开了一些节点,如果直接建树,有可能父亲节点还没有建,当前点就建立了。果然我还是太水了。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100003
#define M 6000000
using namespace std;
int a1[N],p[N],b1[N],n,m;
int v[N*2],point[N],next[N*2],tot;
int fa[N][20],mi[20],deep[N],sz,cnt,num[N],pos[N];
int ls[M],rs[M],sum[M],root[N],nm,pd[N];
int cmp(int x,int y)
{
	return a1[x]<a1[y];
}
void add(int x,int y)
{
   tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
   tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x;	
}
void dfs(int x,int f)
{
	pos[x]=++sz; num[sz]=x; 
	for (int i=1;i<=17;i++)
	 {
	 	if (deep[x]-mi[i]<0) break;
	 	fa[x][i]=fa[fa[x][i-1]][i-1];
	 }
	for (int i=point[x];i;i=next[i])
	 if (v[i]!=f)
	 {
	 	fa[v[i]][0]=x;
	 	deep[v[i]]=deep[x]+1;
	 	dfs(v[i],x);
	 }
}
void build(int l,int r,int fa,int &k,int v)
{
	k=++nm; ls[k]=ls[fa]; rs[k]=rs[fa];
	int mid=(l+r)/2;
	sum[k]=sum[fa]+1;
	if (l==r) return ;
	if (v<=mid)
	 build(l,mid,ls[fa],ls[k],v);
	else
	 build(mid+1,r,rs[fa],rs[k],v);
}
int lca(int x,int y)
{
	if (deep[x]<deep[y])
	 swap(x,y);
	int k=deep[x]-deep[y];
	for (int i=0;i<=17;i++)
	 if ((k>>i)&1)
	  x=fa[x][i];
	if (x==y) return x;
	for (int i=17;i>=0;i--)
	 if (fa[x][i]!=fa[y][i])
	  {
	  	x=fa[x][i];
	  	y=fa[y][i];
	  } 
	return fa[x][0];
}
int query(int x,int y,int k)
{
	int c=lca(x,y); int d=fa[c][0];
	int l=1; int r=cnt;
	int a=root[pos[x]]; int b=root[pos[y]]; c=root[pos[c]]; d=root[pos[d]];
	while (l<r) 
	 {
	 	int tmp=sum[ls[a]]+sum[ls[b]]-sum[ls[c]]-sum[ls[d]];
	 	int mid=(l+r)/2;
	 	if (k<=tmp)
	 	 {
	 	 	a=ls[a]; b=ls[b]; c=ls[c]; d=ls[d];
	 	 	r=mid; 
	 	 }
	 	else
	 	 {
	 	 	a=rs[a]; b=rs[b]; c=rs[c]; d=rs[d];
			l=mid+1; k-=tmp;
	 	 }
	 }
	return a1[pd[l]];
} 
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	 scanf("%d",&a1[i]),p[i]=i;
	sort(p+1,p+n+1,cmp);
    cnt=0;
	for (int i=1;i<=n;i++)
	 if (a1[p[i]]!=a1[p[i-1]])
	  cnt++,b1[p[i]]=cnt,pd[cnt]=p[i];
	 else
	  b1[p[i]]=cnt;
	for (int i=1;i<n;i++)
	 {
	 	int x,y;scanf("%d%d",&x,&y);
	 	add(x,y);
	 }
	deep[1]=1; mi[0]=1;
	for (int i=1;i<=17;i++)
	 mi[i]=mi[i-1]*2;
	dfs(1,0);
	for (int i=1;i<=n;i++)
	{
	 int t=num[i];
	 build(1,cnt,root[pos[fa[t][0]]],root[i],b1[t]);
    }
	int last=0;
	for (int i=1;i<=m;i++)
	 {
	 	int x,y,z;
	 	scanf("%d%d%d",&x,&y,&z);
	    x^=last; 
		last=query(x,y,z);	
		if (i!=m)  printf("%d\n",last);
		else printf("%d",last);
	 }
} 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值