[主席树] BZOJ2588: Spoj 10628. Count on a tree

题意

询问树上路径第k小点权。

题解

算是主席树模板题。
按点权的值域建线段树,遍历一趟树,对每个节点记下它到根节点的信息。
显然,从x到y的信息=从x到根的信息+从y到根的信息-从 lca(x,y) 到根的信息-从 fa(lca(x,y)) 到根的信息。
直接在对应的树上找第k小即可。记得要离散。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005, maxe=200005;
typedef long long LL;
struct node{
    int L,R,sum; node* ch[2];
    node(int x1=0,int x2=0,int x3=0,node* x4=NULL){ L=x1; R=x2; sum=x3; ch[0]=ch[1]=x4; }
    void maintain(){ sum=ch[0]->sum+ch[1]->sum; }
} nil, *null=&nil;
void init_null(){ null->sum=0; null->ch[0]=null->ch[1]=null; }
typedef node* P_node;
P_node Updata(P_node pre,int val){
    P_node p=new node(pre->L,pre->R,pre->sum,null); 
    p->ch[0]=pre->ch[0]; p->ch[1]=pre->ch[1];
    if(p->L==p->R){ p->sum++; return p; }
    int mid=(p->L+p->R)>>1;
    if(val<=mid) p->ch[0]=Updata(p->ch[0],val);
            else p->ch[1]=Updata(p->ch[1],val);
    p->maintain(); return p;
}
LL w[maxn],b[maxn];
int Query(P_node p1,P_node p2,P_node p3,P_node p4,int k){
    if(p1->L==p1->R) return b[p1->L];
    int suml=p1->ch[0]->sum+p2->ch[0]->sum-p3->ch[0]->sum-p4->ch[0]->sum;
    if(suml>=k) return Query(p1->ch[0],p2->ch[0],p3->ch[0],p4->ch[0],k);
    return Query(p1->ch[1],p2->ch[1],p3->ch[1],p4->ch[1],k-suml);
}
P_node build(int L,int R){
    P_node p=new node(L,R,0,null);
    if(L==R) return p;
    int mid=(L+R)>>1;
    p->ch[0]=build(L,mid); p->ch[1]=build(mid+1,R);
    p->maintain(); return p;
}

int n,m,fir[maxn],nxt[maxe],son[maxe],tot;
int anc[maxn][20],dep[maxn];
void add(int x,int y){
    son[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot;
}
void dfs_LCA(int x,int pre){
    anc[x][0]=pre; for(int i=1;i<=17;i++) anc[x][i]=anc[anc[x][i-1]][i-1]; 
    for(int j=fir[x];j;j=nxt[j]) if(son[j]!=pre) dep[son[j]]=dep[x]+1, dfs_LCA(son[j],x);
}
int getLCA(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=17;i>=0;i--) if(dep[anc[x][i]]>=dep[y]) x=anc[x][i];
    if(x==y) return x;
    for(int i=17;i>=0;i--) if(anc[x][i]!=anc[y][i]) x=anc[x][i], y=anc[y][i];
    return anc[x][0];
}
int find(LL x){
    int L=1,R=b[0];
    while(L<=R){
        int mid=(L+R)>>1; if(b[mid]==x) return mid;
        if(b[mid]<x) L=mid+1; else R=mid-1;
    }
}
P_node rt[maxn];
void dfs(int x,int pre){
    rt[x]=Updata(rt[pre],find(w[x]));
    for(int j=fir[x];j;j=nxt[j]) if(son[j]!=pre) dfs(son[j],x);
}
LL getint(){
    char ch=getchar(); LL res=0,ff=1;
    while(!('0'<=ch&&ch<='9')){ if(ch=='-') ff=-1; ch=getchar(); }
    while('0'<=ch&&ch<='9') res=res*10+ch-'0', ch=getchar();
    return res*ff;
}
int main(){
    freopen("bzoj2588.in","r",stdin);
    freopen("bzoj2588.out","w",stdout);
    init_null();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) b[i]=w[i]=getint(); b[0]=n;
    for(int i=1;i<=n-1;i++){
        int x=getint(),y=getint();
        add(x,y); add(y,x);
    }
    dfs_LCA(1,1);
    sort(b+1,b+1+b[0]); b[0]=unique(b+1,b+1+b[0])-(b+1); 
    rt[0]=build(1,b[0]);
    dfs(1,0);
    LL last_ans=0;
    while(m--){
        LL x=getint(),y=getint(),z=getint(); x^=last_ans;
        int lca=getLCA(x,y), lca_fa=lca==1?0:anc[lca][0]; 
        printf("%lld",last_ans=Query(rt[x],rt[y],rt[lca],rt[lca_fa],z)); if(m) printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值