UOJ Logo SHYI的博客

博客

Spoj 10628. Count on a tree 题解

2016-07-30 23:01:37 By SHYI

题目大意:给定一棵n个点的树,每个点有一个权值,m个询问,每次询问树上点x到点y的路径上的第k小数。

思路:dfs后给每个节点一个dfs序,以每个点在他父亲的基础上建立主席树,询问时用(点x+点y-点lca(x,y)-点dad[lca(x,y)])即可得到x到y的链,在上面查询即可。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 200009
using namespace std;

int tot=1,dfn,num,cnt,pa[N][18],to[N],next[N],head[N],lc[N*10],rc[N*10],deep[N],sum[N*10],id[N],pos[N],root[N],a[N],b[N];

int read()
{
    int x=0,y=1;char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') y=-1;ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-48;ch=getchar();}
    return x*y;
}

void add(int x,int y)
{
     to[++cnt]=y,next[cnt]=head[x],head[x]=cnt;
}

void dfs(int x)
{
     int i;id[x]=++dfn,pos[dfn]=x;
     for (i=1;i<=16;i++)
         if ((1<<i)<=deep[x]) pa[x][i]=pa[pa[x][i-1]][i-1];
         else break;
     for (i=head[x];i;i=next[i])
         if (pa[x][0]!=to[i])
         {
              deep[to[i]]=deep[x]+1;
              pa[to[i]][0]=x;
              dfs(to[i]);
         }
}

void change(int l,int r,int x,int &cur,int _cur)
{
     cur=++num;
     lc[cur]=lc[_cur];
     rc[cur]=rc[_cur];
     sum[cur]=sum[_cur]+1;
     if (l==r) return;
     int mid=l+r>>1;
     if (x<=b[mid]) change(l,mid,x,lc[cur],lc[_cur]);
     else change(mid+1,r,x,rc[cur],rc[_cur]);
}

int LCA(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int i,t=deep[x]-deep[y];
    for (i=0;i<=16;i++)
        if ((1<<i)&t) x=pa[x][i];
    for (i=16;i>=0;i--)
        if (pa[x][i]!=pa[y][i]) x=pa[x][i],y=pa[y][i];
    if (x==y) return x;
    return pa[x][0];
}

int ask(int x,int y,int k)
{
    int a=root[id[x]],b=root[id[y]],c=LCA(x,y),d=pa[c][0],l=1,r=tot;
    c=root[id[c]],d=root[id[d]];
    while (l<r)
    {
          int t=sum[lc[a]]+sum[lc[b]]-sum[lc[c]]-sum[lc[d]],mid=l+r>>1;
          if (t>=k) a=lc[a],b=lc[b],c=lc[c],d=lc[d],r=mid;
          else a=rc[a],b=rc[b],c=rc[c],d=rc[d],l=mid+1,k-=t;
    }
    return l;
}

int main()
{
    int n=read(),m=read(),i,x,y,ans=0,k;
    for (i=1;i<=n;i++) a[i]=read(),b[i]=a[i];
    for (i=1;i<n;i++) x=read(),y=read(),add(x,y),add(y,x);
    dfs(1),sort(b+1,b+n+1);
    for (i=2;i<=n;i++)
        if (b[tot]!=b[i]) b[++tot]=b[i];
    for(i=1;i<=n;i++) change(1,tot,a[pos[i]],root[i],root[id[pa[pos[i]][0]]]);
    for (i=1;i<=m;i++)
    {
        x=read(),y=read(),k=read(),x^=ans;
        printf("%d",ans=b[ask(x,y,k)]);
        if (i<m) printf("\n");
    }
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。