您的位置:首页 > 其它

HYSBZ-2588- Count on a tree (主席树)

2017-09-07 18:56 155 查看

Spoj 10628. Count on a tree

Time Limit: 12 Sec  Memory Limit:128 MB
Submit: 7148  Solved: 1739

[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

暴力自重。。。

中文题面就不讲意思了;
思路:这题就是找树上一条链上的第k小值,所以把每个点到根节点这些链建主席树,然后会发现u,v两个节点之间的链,在最后利用主席书计算的时候,主席树上记录的值等于u->root(根)这条链加上v->root减去lca[u,v]->root减去fa[lca[u,v]]->root,画个图想一下就懂了,这题就是在树上建主席树 ,算个入门题把;主席树刚学几天,写的时候写了个错误wa了好多发~~~这题求lca我是用树链剖分求的,倍增法还不会,并且感觉树链剖分求lca很好理解;
AC代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define maxn 100010
using namespace std;
int n,m;
int arr[maxn],b[maxn];
int dep[maxn],son[maxn],sz[maxn],fa[maxn],id[maxn],top[maxn],head[maxn];//树链剖分里面的一些数组
int tot,num,cnt,_sz;
int root[maxn*35],lson[maxn*35],rson[maxn*35],sum[maxn*35];//根,左儿子,右儿子,记录数字个数
struct Edge{int to,next;}edge[maxn*2];
void add_edge(int u,int v)//相当于建题目给的图
{
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void add_tree(int &rot,int pre,int id,int l,int r)//建当前点到root的主席树,由它的父亲得到
{
rot=++cnt;//动态申请一个点
sum[rot]=sum[pre]+1;//数字的个数加1
lson[rot]=lson[pre];//左右儿子直接继承父亲的
rson[rot]=rson[pre];
if(l==r) return;
int mid=(l+r)>>1;
if(id<=mid) add_tree(lson[rot],lson[pre],id,l,mid);
else add_tree(rson[rot],rson[pre],id,mid+1,r);
}
//树链剖分部分~~~~~
void dfs1(int u,int f,int deep)
{
son[u]=0;sz[u]=1;
fa[u]=f;dep[u]=deep;
add_tree(root[u],root[f],arr[u],1,_sz);
for(int i=head[u];i!=-1;i=edge[i].next)
{
int ff=edge[i].to;
if(ff==fa[u]) continue;
dfs1(ff,u,deep+1);
sz[u]+=sz[ff];
if(sz[son[u]]<sz[ff])
son[u]=ff;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;
id[u]=++num;
if(son[u]) dfs2(son[u],tp);
for(int i=head[u];i!=-1;i=edge[i].next){
int ff=edge[i].to;
if(ff==fa[u]||ff==son[u]) continue;
dfs2(ff,ff);
}
}
int LCA(int u,int v)
{
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) v=fa[top[v]];
else u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
int query(int u,int v,int lca,int flca,int l,int r,int k)
{
if(l==r) return b[l];
int tmp=sum[lson[u]]+sum[lson[v]]-sum[lson[lca]]-sum[lson[flca]];
int mid=(l+r)>>1;
if(tmp>=k) return query(lson[u],lson[v],lson[lca],lson[flca],l,mid,k);
else return query(rson[u],rson[v],rson[lca],rson[flca],mid+1,r,k-tmp);
}
void init()
{
tot=num=cnt=0;
memset(head,-1,sizeof(head));
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=1;i<=n;i++)
scanf("%d",&arr[i]),b[i]=arr[i];
sort(b+1,b+n+1);
_sz=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;i++)
arr[i]=lower_bound(b+1,b+_sz+1,arr[i])-b;//在这里犯了个错误,b数组去重后数组个数变成了_sz个,开始写的b+n+1,结果错了几发
int x,y;
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}
dfs1(1,0,1);
dfs2(1,1);
int last=0;
for(int i=1;i<=m;i++)
{
int u,v,k;
scanf("%d%d%d",&u,&v,&k);
u=u^last;
int lca=LCA(u,v);
last=query(root[u],root[v],root[lca],root[fa[lca]],1,_sz,k);
printf("%d",last);
if(i!=m) printf("\n");//格式注意一下
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: