2588: Spoj 10628. Count on a tree

题目链接

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

题解:树上每个结点建一棵权值线段树记录其到根所有点的权值
查询(a,b)时,查询的是线段树a+线段树b-线段树lca(a,b)-线段树father[lca(a,b)],在权值线段树上求第k小即可

我的收获:常规操作……

#include <bits/stdc++.h>
using namespace std;

#define rep(i,x,y) for(int i=(x);i<=(y);i++)
#define gep(i,x,y) for(int i=(x);i>=(y);i--)

const int N=100005;
const int L=20;

int n,m,last,cnt,sz;
int v[N],tmp[N];
int fa[N][L+2];
int dep[N],root[N];

vector<int> E[N];

namespace ChairmanTree{

int tl[N*50],tr[N*50],sum[N*50];

void update(int x,int &now,int l,int r,int k){
    now=++sz;sum[now]=sum[x]+1,tl[now]=tl[x],tr[now]=tr[x];
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(k<=mid) update(tl[x],tl[now],l,mid,k);
    else update(tr[x],tr[now],mid+1,r,k);
}

int query(int a,int b,int c,int d,int l,int r,int k){
    if(l==r) return l;
    int mid=(l+r)>>1;
    int t=sum[tl[a]]+sum[tl[b]]-sum[tl[c]]-sum[tl[d]];
    if(t>=k) return query(tl[a],tl[b],tl[c],tl[d],l,mid,k);
    return query(tr[a],tr[b],tr[c],tr[d],mid+1,r,k-t);
}

}

void predfs(int x)
{ 
    ChairmanTree::update(root[fa[x][0]],root[x],1,cnt,v[x]);
    rep(i,1,L) fa[x][i]=fa[fa[x][i-1]][i-1];
    rep(i,0,E[x].size()-1){
        int v=E[x][i];
        if(v==fa[x][0]) continue;
        dep[v]=dep[x]+1,fa[v][0]=x;
        predfs(v);
    }
}

int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    rep(i,0,L) if((dep[x]-dep[y])&(1<<i)) x=fa[x][i];
    if(x==y) return x;
    gep(i,L,0) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

void work()
{
    int x,y,z;
    rep(i,1,m)
    {
        scanf("%d%d%d",&x,&y,&z);x^=last;
        int c=lca(x,y);
        last=tmp[ChairmanTree::query(root[x],root[y],root[c],root[fa[c][0]],1,cnt,z)];
        printf("%d",last);
        if(i!=m) putchar('\n');
    }
}

void init()
{
    scanf("%d%d",&n,&m);
    rep(i,1,n) scanf("%d",&v[i]),tmp[i]=v[i];
    sort(tmp+1,tmp+1+n);cnt=unique(tmp+1,tmp+1+n)-tmp-1;
    rep(i,1,n) v[i]=lower_bound(tmp+1,tmp+1+cnt,v[i])-tmp;
    int x,y;rep(i,2,n) scanf("%d%d",&x,&y),E[x].push_back(y),E[y].push_back(x);
    predfs(1);
}

int main()
{
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值