• 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
    ",Ask(rt[x],rt[y],rt[lca],rt[f[lca][0]],1,n,k));
        }
        return 0;
    } 
  • 相关阅读:
    Linux tmux 工具
    HTML 注释
    HTML 引用
    HTML 格式化
    /etc/services
    Linux ss 命令
    Python cookielib 模块
    爬取需要登录的页面
    hasattr() 、getattr() 、setattr()
    爬取文本
  • 原文地址:https://www.cnblogs.com/Never-mind/p/7716327.html
Copyright © 2020-2023  润新知