[BZOJ2588/SPOJ10628]Count on a tree

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
暴力自重。。。


(其实SPOJ原题是不需要异或Lastans的……)

并不算难的主席树,记root[i]记录i到根路径上的所有点即可。查询的时候lca不能算两次,其余的就是求第k大的板子了。。。

然后如果碰到无限RE的,可能不是数组开小了,可能是你答案求错了,然后异或Lastans的时候就出事了。。。

我建主席树居然是for循环建的,脑子抽了。。。改了1h。。。显然要dfs建主席树啊。。。

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)  print(x/10);
    putchar(x%10+'0');
}
const int N=1e5,M=1e7;
int root[N+10],list[N+10],val[N+10];
struct Segment{
    int ls[M+10],rs[M+10],cnt[M+10],tot;
    void insert(int &k,int p,int l,int r,int v){
        cnt[k=++tot]=cnt[p]+1;
        ls[k]=ls[p],rs[k]=rs[p];
        if (l==r)   return;
        int mid=(l+r)>>1;
        if (v<=mid) insert(ls[k],ls[p],l,mid,v);
        else    insert(rs[k],rs[p],mid+1,r,v);
    }
    int Query(int u,int v,int lca,int lca_fa,int l,int r,int x){
        if (l==r)   return list[l];
        int mid=(l+r)>>1,tmp=cnt[ls[u]]+cnt[ls[v]]-cnt[ls[lca]]-cnt[ls[lca_fa]];
        if (x<=tmp) return Query(ls[u],ls[v],ls[lca],ls[lca_fa],l,mid,x);
        else    return Query(rs[u],rs[v],rs[lca],rs[lca_fa],mid+1,r,x-tmp);
    }
}Chairman;
struct S1{
    int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot;
    int fa[N+10],size[N+10],top[N+10],deep[N+10],Rem[N+10];
    void connect(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
    void insert(int x,int y){connect(x,y),connect(y,x);}
    void build(int x){
        deep[x]=deep[fa[x]]+1,size[x]=1;
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]) continue;
            fa[son]=x,build(son),size[x]+=size[son];
            if (size[son]>size[Rem[x]]) Rem[x]=son;
        }
    }
    void dfs(int x){
        if (!x) return;
        top[x]=Rem[fa[x]]==x?top[fa[x]]:x;
        dfs(Rem[x]);
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]||son==Rem[x])    continue;
            dfs(son);
        }
    }
    void Segment_build(int x,int T){
        Chairman.insert(root[x],root[fa[x]],1,T,val[x]);
        for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
            if (son==fa[x]) continue;
            Segment_build(son,T);
        }
    }
    int LCA(int x,int y){
        while (top[x]!=top[y]){
            if (deep[top[x]]<deep[top[y]])  swap(x,y);
            x=fa[top[x]];
        }
        return deep[x]<deep[y]?x:y;
    }
}Tree;
int main(){
    int n=read(),m=read();
    for (int i=1;i<=n;i++)  val[i]=list[i]=read();
    sort(list+1,list+1+n);
    int T=unique(list+1,list+1+n)-list-1;
    for (int i=1;i<=n;i++)  val[i]=lower_bound(list+1,list+1+T,val[i])-list;
    for (int i=1;i<n;i++){
        int x=read(),y=read();
        Tree.insert(x,y);
    }
    Tree.build(1),Tree.dfs(1),Tree.Segment_build(1,T);
    int LastAns=0;
    for (int i=1;i<=m;i++){
        int u=read()^LastAns,v=read(),k=read();
        int lca=Tree.LCA(u,v);
        printf("%d\n",LastAns=Chairman.Query(root[u],root[v],root[lca],root[Tree.fa[lca]],1,T,k));
    }
    return 0;
}

转载于:https://www.cnblogs.com/Wolfycz/p/9579959.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值