bzoj2588 Spoj 10628. Count on a tree

http://www.elijahqi.win/archives/1710
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

暴力自重。。。
主席树
针对权值离散化 然后从树根开始向每个点建权值线段树然后从他的父节点把root继承过来
然后统计第k大的时候
然后用u v lca(u,v) father(lca(u,v))然后用线段树加加减减 在树上二分的去找第k大然后输出即可

#include<cstdio>
#include<algorithm>
#define N 110000
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline long long read(){
    long long x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc(); }
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
struct node{
    int left,right,size;
}tree[20*N];
struct node1{
    int y,next;
}data[N<<1];
int n,m,h[N],fa[N][22],Log[N],dep[N],w[N],w1[N],ww[N],num,root[N],nn;
inline void update(int x){
    tree[x].size=tree[tree[x].left].size+tree[tree[x].right].size;
}
inline void build(int &x,int l,int r,int pos){
    tree[++num]=tree[x];x=num;
    if (l==r) {tree[x].size++;return;}
    int mid=l+r>>1;
    if (pos<=mid) build(tree[x].left,l,mid,pos);else build(tree[x].right,mid+1,r,pos);update(x);
}
void dfs(int x){
    root[x]=root[fa[x][0]];build(root[x],1,nn,w1[x]);
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (fa[x][0]==y) continue;fa[y][0]=x;dep[y]=dep[x]+1;
        for (int j=1;j<=Log[dep[y]];++j) fa[y][j]=fa[fa[y][j-1]][j-1];dfs(y);
    }
}
inline int lca(int x,int y){
    if (dep[x]>dep[y]) swap(x,y);
    int dis=dep[y]-dep[x];
    for (int i=0;i<=Log[dis];++i) if (dis&(1<<i)) y=fa[y][i];
    if (x==y) return x;
    for (int j=Log[dep[y]];j>=0;--j) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
inline int query(int rt1,int rt2,int rt3,int rt4,int l,int r,int k){
    if (l==r) return ww[l];int mid=l+r>>1;
    int l1=tree[rt1].left,l2=tree[rt2].left,l3=tree[rt3].left,l4=tree[rt4].left;
    int r1=tree[rt1].right,r2=tree[rt2].right,r3=tree[rt3].right,r4=tree[rt4].right;
    int sizel=tree[l1].size+tree[l2].size-tree[l3].size-tree[l4].size;
    if (k<=sizel) return query(l1,l2,l3,l4,l,mid,k);else return query(r1,r2,r3,r4,mid+1,r,k-sizel);
}
void print(int x,int l,int r){
    int mid=l+r>>1;
    if(tree[x].left) print(tree[x].left,l,mid);
    if (l==r) printf("%d:%d ",l,tree[x].size);
    if (tree[x].right) print(tree[x].right,mid+1,r);
}
void dfs1(int x){
    printf("%d\n",x);print(root[x],1,nn);printf("\n");
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (fa[x][0]==y) continue;dfs1(y);
    }
}int ans;
int main(){
    //freopen("bzoj2588.in","r",stdin);
    n=read();m=read();
    for (int i=1;i<=n;++i) w[i]=read(),ww[i]=w[i];
    sort(ww+1,ww+n+1);nn=unique(ww+1,ww+n+1)-ww-1;
    for (int i=1;i<=n;++i) w1[i]=lower_bound(ww+1,ww+nn+1,w[i])-ww;
    for (int i=1;i<n;++i){
        int x=read(),y=read();
        data[++num].y=y;data[num].next=h[x];h[x]=num;
        data[++num].y=x;data[num].next=h[y];h[y]=num;
    }Log[0]=-1;for (int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
    num=0;dfs(1);
    for (int i=1;i<=m;++i){
        int x=read(),y=read(),k=read();x^=ans;if (y>x) swap(x,y);
        int l=lca(x,y);ans=query(root[x],root[y],root[l],root[fa[l][0]],1,nn,k);
        if (i<m) printf("%d\n",ans);else printf("%d",ans);
    }
    //dfs1(1);
    return 0;
}
/*
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
2 5 2
2 5 3
2 5 4
7 8 2


9 1
1 6 5 3 3 10 3 7 9
1 3
8 3
4 1
6 5
5 4
5 9
3 2
3 7
8 9 4

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值