[BZOJ]2588 Spoj 10628. Count on a tree 树链第k大 PE的进来看看

15 篇文章 0 订阅
2 篇文章 0 订阅

2588: Spoj 10628. Count on a tree

Time Limit: 12 Sec Memory Limit: 128 MB
Submit: 6686 Solved: 1596
[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

暴力自重。。。

几句闲话

这道题太坑了,PE的原因是最后一行不能输出换行…
这道题网上的数组版懒都懒得看,自己喜欢用指针版+人工null,感觉思路清晰一点…但是自己从来没有碰过主席树,区间第k大都是直接用树状数组套线段树水过去的.这次一来就来一个树上主席树,感觉有点陡,又不想去看网上的博客重新学习一下,问了问学长大概的原理,就趴在电脑面前YY。YY了一会儿开始打代码,顺着自己以前求区间第k大的思路加上主席树的原理,成功过了样例…yeah,YY都YY出来了,太爽了,交上去..
然后就是一页的RE和PE….关键是我还不知道PE是什么意思,还以为代码出错了,YY失败…最后发现是这个PE的原因,马上就流金哇呀酷烈啊…

正文

好吧,这道题我们的思路是在每一个节点建一棵从这里到根的值域线段树,每个值域线段用sum记录这个节点到跟的点权在这个至于范围内的有多少个线段。但是就算离散化后空间肯定爆炸,所以说主席树就是干这玩意的——省空间的.当前节点要建的与father(父亲节点,简称fa)的值域线段树实际上只有一个元素之差,就是当前节点的点值.那么我们就可以省很多空间.

Q1:怎么省?
Answer:我们想象建当前节点的值域线段树就是在father节点的已有的值域线段树里面插这个节点的值,那么如果说这个当前节点的val在lf到mid这段值域范围之内,那么val只会对这个lf到mid的至于线段的sum做出贡献,mid+1到rg并不受影响,那么我们直接把当前这个节点的值域线段树里这段mid+1到rg的值域线段等同于fa节点离得就可以了,因为没有这一个元素的影响,这段sum肯定是一样的.用代码就是 now->rson=fa->rson,rson就是mid+1到rg的值域线段,我们now(管理lf到rg的值域)只需指针指过去就可以了.
这就是主席树的思想.

Q2:有了主席树,怎么算链的点值第k大呢?
Answer:先从一个普通序列入手。我们把Q1讨论的fa和当前节点想成普通序列上的 i-1 和 i,第i个数上的值域线段树维护是从i到1的.对于一段区间l到r,我们将r的值域线段树与l的值域线段树的sum相减,就会得到l到r这段区间每个值域的sum,相当于是一棵新的临时值域线段树,那么我们在这棵值域线段树里找排名,如果k比左值域线段(即区间)的sum大的话,那就在右区间里找,只是要k=k-左区间的sum,因为现在是去右区间里找右区间管理的值域是大于左区间管理的值域.否则k不变去左区间里找.
链的第k大相当于我们只要得到u到v这条链的值域线段树就可以了.从这个节点到1,相当于序列中就是从i到1.那么相当于
1.u的值域线段树-fa[lca(u,v)]的值域线段树=u到lca的值域线段树.
再用
2.v的值域线段树-[lca]值域线段树树=v到lca下一个点的值域线段树.
1+2就组成整条(u,v)链的值域线段树了,再向普通序列一样在左右区间找.

Q3:是真的相减得到一棵新的临时值域线段树吗?
Answer:实际上只是每次u,v,lca,fa[lca]都进入到lson或rson,每次都做减法,结果与k比较,再判断去lson和rson而已,不是真的建了一棵值域线段树.

注意离散化.

#include<stdio.h>
#include<algorithm>
const int maxn=100005;
const int maxnn=600000;
typedef long long dnt;
int seq[maxnn],in[maxn],st[maxnn][20],h[maxn],fa[maxn],discretization[maxn],tot,num,indexx,ans;
int x,y,va[maxn],vv[maxn],n,m;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    register int x=0;
    register char ch=nc();
    while(ch<'0'||ch>'9')ch=nc();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=nc();
    return x;
}
struct edge{
    int nxt,v;
}e[maxn*4];
void add(int u,int v){
    e[++num].v=v;
    e[num].nxt=h[u];
    h[u]=num;
}
inline void stha(){
    for(register int i=1;i<=indexx;i++) st[i][0]=in[seq[i]];
    for(int j=1;(1<<j)<indexx;j++)
     for(int i=1;i+(1<<j)-1<=indexx;i++)
      st[i][j]=std::min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline int query(int L,int R){
    if(L>R) std::swap(L,R);
    int len=R-L+1;
    int k=0,ans;
    while((1<<(k+1))<len) k++;
    ans=std::min(st[L][k],st[R-(1<<k)+1][k]);
    return seq[ans];
}
int find_position(int x){
    int lf=1,rg=tot;
    while(lf<rg){
        int mid=(lf+rg)>>1;
        if(discretization[mid]<x) lf=mid+1;
        else rg=mid; 
    }
    return lf;
}
struct node{
    node *ls,*rs;
    int sum;
    void update(){sum=ls->sum+rs->sum;}
}pool[maxnn*5],*tail=pool,*root[maxn*2],*null;
inline void init(){
    null=++tail;
    null->ls=null;
    null->rs=null;
    null->sum=0;
}
node *newnode() {
    node *bt=++tail;
    bt->ls=null;
    bt->rs=null;
    bt->sum=0;
    return bt;
}
void build(node *pre,node *now,int lf,int rg,int pos){
    if(lf==rg) {now->sum=pre->sum+1;return;}
    int mid=(lf+rg)>>1;
    if(pos<=mid){
       now->rs=pre->rs;
       if(now->ls==null) now->ls=newnode();
       build(pre->ls,now->ls,lf,mid,pos);
       now->update();
    }
    else{
       now->ls=pre->ls;
       if(now->rs==null) now->rs=newnode();
       build(pre->rs,now->rs,mid+1,rg,pos);
       now->update();
    }
}
int queryend(int lf,int rg,node *a,node *b,node *c,node *d,int k){
    if(lf==rg) return discretization[lf];
    int tmp=a->ls->sum+b->ls->sum-c->ls->sum-d->ls->sum;
    int mid=(lf+rg)>>1;
    if(tmp>=k) return queryend(lf,mid,a->ls,b->ls,c->ls,d->ls,k);
    else return queryend(mid+1,rg,a->rs,b->rs,c->rs,d->rs,k-tmp);
}
void dfs(int u,int f){
    build(root[fa[u]],root[u],1,tot,va[u]);
    seq[++indexx]=u;
    in[u]=indexx;
    for(int i=h[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==f) continue;
        fa[v]=u;
        dfs(v,u);
        seq[++indexx]=u;
    }
}
int main(){
    init();
    n=read(),m=read();
    for(register int i=1;i<=n;i++) vv[i]=va[i]=read();
    std::sort(vv+1,vv+n+1);
    discretization[++tot]=vv[1];
    for(register int i=2;i<=n;i++) if(vv[i]!=vv[i-1]) discretization[++tot]=vv[i];
    for(register int i=1;i<=n;i++) va[i]=find_position(va[i]);
    for(register int i=1;i<n;i++) x=read(),y=read(),add(x,y),add(y,x);
    for(register int i=0;i<=n;i++) root[i]=newnode();
    fa[1]=0;
    dfs(1,0);
    stha();
    while(m--){
       int k;
       x=read(),y=read(),k=read();
       x=x^ans;
       int lca;
       lca=query(in[x],in[y]);
       ans=queryend(1,tot,root[x],root[y],root[lca],root[fa[lca]],k);
       if(m!=0) printf("%d\n",ans);
       else printf("%d",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值