caioj1443:第k小的数Ⅲ

【传送门:caioj1443


简要题意:

  给出一颗n个点的树,给出每个点的权值,再给出n-1条边,有m个询问,每个询问输入x,y,k,输出第x节点到第y节点的路径上第k大的点


题解:

  这是一道主席树的例题,感觉很想用树链剖分,但是会超时吧......

  做法就是将每个点到树的根所形成的链建立线段树,然后dfs找出点与点之间的父子关系,每个点都将自己和自己的父亲,自己的父亲的父亲....直到根所组成的所有线段树合并起来,然后就可以得到能够代表区间的主席树了,然后每当询问输入x,y时,就先找x和y的最近公共祖先(倍增LCA来求),然后x和y的路径其实就是x到根的路径加上y到根的路径,然后减去最近公共祖先到根的路径,再减去最近公共祖先的父亲到根的路径(为什么是最近公共祖先的父亲呢,因为如果是再减去最近公共祖先到根的路径的话,最近公共祖先就会被除去,这样是不对的,所以减去最近公共祖先的父亲到根的路径)


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int s[110000],ss[110000];
struct trnode
{
    int lc,rc,c;
}tr[2100000];int cnt;
int rt[110000];
struct edge
{
    int x,y,next;
}a[210000];int len,last[110000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int n;
int LS(int d)
{
    int l=1,r=n,ans=0;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(s[mid]<=d)
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    return ans;
}
void Link(int &u,int l,int r,int p)
{
    if(u==0) u=++cnt;
    tr[u].c++;
    if(l==r) return ;
    int mid=(l+r)/2;
    if(p<=mid) Link(tr[u].lc,l,mid,p);
    else Link(tr[u].rc,mid+1,r,p);
}
void Merge(int &u1,int u2)
{
    if(u1==0){u1=u2;return ;}
    if(u2==0) return ;
    tr[u1].c+=tr[u2].c;
    Merge(tr[u1].lc,tr[u2].lc);
    Merge(tr[u1].rc,tr[u2].rc);
}
int dep[110000];
int f[110000][21];
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=20;i>=0;i--) if(dep[x]-dep[y]>=(1<<i)) x=f[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--)
        if(dep[x]>=(1<<i)&&f[x][i]!=f[y][i])
        {
            x=f[x][i];y=f[y][i];
        }
    return f[x][0];
}
void bt(int x,int fa)
{
    dep[x]=dep[fa]+1;
    f[x][0]=fa;
    for(int i=1;(1<<i)<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1];
    Merge(rt[x],rt[fa]);
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=fa)
        {
            bt(y,x);
        }
    }
}
int Ask(int u1,int u2,int u3,int u4,int l,int r,int p)
{
    if(l==r) return s[l];
    int c=tr[tr[u1].lc].c+tr[tr[u2].lc].c-tr[tr[u3].lc].c-tr[tr[u4].lc].c;
    int mid=(l+r)/2;
    if(p<=c) return Ask(tr[u1].lc,tr[u2].lc,tr[u3].lc,tr[u4].lc,l,mid,p);
    else return Ask(tr[u1].rc,tr[u2].rc,tr[u3].rc,tr[u4].rc,mid+1,r,p-c);
}
int main()
{
    int m;
    scanf("%d%d",&n,&m);
    cnt=0;memset(rt,0,sizeof(rt));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&ss[i]);
        s[i]=ss[i];
    }
    sort(s+1,s+n+1);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        ins(x,y);ins(y,x);
    }
    cnt=0;
    for(int i=1;i<=n;i++) Link(rt[i],1,n,LS(ss[i]));
    dep[0]=0;bt(1,0);
    for(int i=1;i<=m;i++)
    {
        int x,y,k;
        scanf("%d%d%d",&x,&y,&k);
        int lca=LCA(x,y);
        printf("%d\n",Ask(rt[x],rt[y],rt[lca],rt[f[lca][0]],1,n,k));
    }
    return 0;
} 
posted @ 2017-10-23 13:33  Star_Feel  阅读(215)  评论(0编辑  收藏  举报