BZOJ2588 - Count on a tree

原题链接

题意简述

给出一棵带点权的n(n105)个节点的树,Q(Q105)次询问路径(u,v)上第k小的点权值。

分析

记节点到根的点权和为dst,则(u,v)上的点权和为dst[u]+dst[v]dst[lca(u,v)]dst[fa[lca(u,v)]]。相似的,对于每个节点,我们用权值线段树记录该节点到根的路径上的点权,这样就可以得到路径(u,v)上的点权了。考虑到每个节点的线段树相当于在其父节点的线段树上进行单点+1,我们可以用可持久化线段树来解决这道题。

实现

与普通的可持久化线段树差别不大,只是在查询的时候要多传几个参数。

代码

//Count on a tree
#include <cstdio>
#include <algorithm>
using namespace std;
inline char gc()
{
    static char now[1<<16],*S,*T;
    if(S==T) {T=(S=now)+fread(now,1,1<<16,stdin); if(S==T) return EOF;}
    return *S++;
}
inline int read()
{
    int x=0,f=1; char ch=gc();
    while(ch<'0'||'9'<ch) {if(ch=='-') f=-1; ch=gc();}
    while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int const N=1e5+10;
int n,m,v[N];
int num,map[N];
struct rec{int v,id;} a0[N];
bool cmpV(rec x,rec y) {return x.v<y.v;}
int cnt,h[N];
struct edge{int v,nxt; edge(int u=0,int v1=0){v=v1,nxt=h[u],h[u]=cnt;}} ed[N<<1];
#define s sg[s0]
int sgCnt,rt[N];
struct seg{int cnt; int L,R;} sg[N*20];
void update(int s0) {s.cnt=sg[s.L].cnt+sg[s.R].cnt;}
void ins(int &s0,int fr,int to,int v)
{
    sg[++sgCnt]=s; s0=sgCnt;
    if(fr==to) {s.cnt++; return;}
    int mid=fr+to>>1;
    if(v<=mid) ins(s.L,fr,mid,v);
    else ins(s.R,mid+1,to,v);
    update(s0);
}
int query(int s1,int s2,int s3,int s4,int fr,int to,int k)
{
    if(fr==to) return fr;
    int cntL=sg[sg[s1].L].cnt+sg[sg[s2].L].cnt-sg[sg[s3].L].cnt-sg[sg[s4].L].cnt;
    int mid=fr+to>>1;
    if(k<=cntL) return query(sg[s1].L,sg[s2].L,sg[s3].L,sg[s4].L,fr,mid,k);
    else return query(sg[s1].R,sg[s2].R,sg[s3].R,sg[s4].R,mid+1,to,k-cntL);
}
int fa[N][20],dpt[N];
void trform(int u)
{
    ins(rt[u]=rt[fa[u][0]],1,num,v[u]);
    for(int i=1;i<=17;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=h[u];i;i=ed[i].nxt)
    {
        int v=ed[i].v;
        if(v!=fa[u][0]) fa[v][0]=u,dpt[v]=dpt[u]+1,trform(v);
    }
}
int lca(int u,int v)
{
    if(dpt[u]<dpt[v]) swap(u,v);
    for(int i=17;i>=0;i--) if(dpt[fa[u][i]]>=dpt[v]) u=fa[u][i];
    for(int i=17;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    return u==v?u:fa[u][0];
}
int main()
{
    n=read(); m=read();
    for(int i=1;i<=n;i++) a0[i].v=read(),a0[i].id=i;
    sort(a0+1,a0+n+1,cmpV);
    num=0;
    for(int i=1;i<=n;i++)
    {
        if(a0[i-1].v!=a0[i].v) map[++num]=a0[i].v;
        v[a0[i].id]=num;
    }
    cnt=0;
    for(int i=1;i<=n-1;i++)
    {
        int u=read(),v=read();
        ed[++cnt]=edge(u,v); ed[++cnt]=edge(v,u);
    }
    rt[0]=sgCnt=0;
    fa[1][0]=0,dpt[1]=1,trform(1);
    int ans=0;
    for(int owo=1;owo<=m;owo++)
    {
        int u=read()^ans,v=read(),k=read(); int w=lca(u,v);
        printf("%d",ans=map[query(rt[u],rt[v],rt[w],rt[fa[w][0]],1,num,k)]);
        if(owo!=m) printf("\n");
    }
    return 0;
}

注意

本题强制在线,查询时仅需要将u异或lastans即可。非强制在线的原题来自SPOJ.com - Problem COT
最后一次查询不要输出\n,否则会PE。

posted @ 2017-12-07 20:34  VisJiao  阅读(85)  评论(0编辑  收藏  举报