BZOJ2588

来源:互联网 发布:淘宝涮单怎么样更赚钱 编辑:程序博客网 时间:2024/06/05 04:59

原题链接

题意简述

给出一棵带点权的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。

原创粉丝点击