Luogu P3834 【模板】可持久化线段树 1(主席树)

Luogu P3834 【模板】可持久化线段树 1(主席树)

HU…终于理解了主席树…

关于主席树学习的几篇blog
  • 1
  • 2
  • 3
  • 4
    主要是前三篇好理解ouo

主要突破时间:2018/5/30下午电脑课(昨天中午效率迷之低。。唉)

好的,现在我也有了模板了

#include<cstdio>
#include<cstring>

int a[2000010],b[2000100],root[2000100];
int len=0,n,m;
struct nod1{int l,r,c,lc,rc;}tr[4000100];

int dfs(int l,int r)
{
    int x=l,y=r,m=b[(x+y)/2];
    while(x<=y)
    {
        while(b[x]<m)x++;
        while(b[y]>m)y--;
        if(x<=y)
        {
            int t=b[x];b[x]=b[y];b[y]=t;
            x++;y--;
        }
    }
    if(l<y)dfs(l,y);
    if(x<r)dfs(x,r);
}

int ef(int x)
{
    int mid,ans;
    int l=1,r=n;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(b[mid]<=x)
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    return ans;
}

void bt(int &rt,int l,int r,int x)
{
    //printf("1111");
    if(rt==0)
    {
        len++;rt=len;
    }
    tr[rt].c++;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(x<=mid)bt(tr[rt].lc,l,mid,x);
    else bt(tr[rt].rc,mid+1,r,x);

}

int add(int &x,int y)
{
    if(x==0)
    {
        x=y;return 0;
    }
    if(y==0)return 0;
    tr[x].c+=tr[y].c;
    add(tr[x].lc,tr[y].lc);
    add(tr[x].rc,tr[y].rc);
}

int find(int x,int y,int l,int r,int k)
{
    if(l==r)return b[l];
    int mid=(l+r)/2,g=tr[tr[x].lc].c-tr[tr[y].lc].c;
    if(k<=g)return find(tr[x].lc,tr[y].lc,l,mid,k);
    else return find(tr[x].rc,tr[y].rc,mid+1,r,k-g);
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    dfs(1,n);//ok
    /*for(int i=1;i<=n;i++)
    {
        printf("%d ",b[i]);
    }printf("\n");*/
    for(int i=1;i<=n;i++)
    {
        //printf("1111");
        bt(root[i],1,n,ef(a[i]));//printf("11111");
        add(root[i],root[i-1]);
    }
    for(int i=1;i<=m;i++)
    {
        int x,y,k;
        scanf("%d %d %d",&x,&y,&k);
        printf("%d\n",find(root[y],root[x-1],1,n,k));
    }
}

注释版

#include<cstdio>
#include<cstring>

int a[2000010],b[2000100],root[2000100];
int len=0,n,m;
struct nod1{int l,r,c,lc,rc;}tr[4000100];

int dfs(int l,int r)
{
    int x=l,y=r,m=b[(x+y)/2];
    while(x<=y)
    {
        while(b[x]<m)x++;
        while(b[y]>m)y--;
        if(x<=y)
        {
            int t=b[x];b[x]=b[y];b[y]=t;
            x++;y--;
        }
    }
    if(l<y)dfs(l,y);
    if(x<r)dfs(x,r);
}

int seat(int x)
{//x是该由哪个叶子结点管 
    int mid,ans;
    int l=1,r=n;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(b[mid]<=x)
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    return ans;
}

void update(int &rt,int l,int r,int x)
{
    if(rt==0)
    {
        len++;rt=len;
    }//少什么才建什么 
    tr[rt].c++;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(x<=mid)update(tr[rt].lc,l,mid,x);
    else update(tr[rt].rc,mid+1,r,x);
    //向左右儿子去 
}

int mix(int &x,int y)
{
    if(x==0)
    //之前没有多建 
    {
        x=y;return 0;
        //那么说明没有少,就直接继承上一棵树的 
    }
    if(y==0)return 0;
    //没有被建,自然不会有左右儿子,不去了 
    tr[x].c+=tr[y].c;
    mix(tr[x].lc,tr[y].lc);
    mix(tr[x].rc,tr[y].rc);
}

int find(int x,int y,int l,int r,int k)
{
    if(l==r)return b[l];
    //找到了,返回该叶子节点掌管的值 
    int mid=(l+r)/2,g=tr[tr[x].lc].c-tr[tr[y].lc].c;
    //相减就是要求范围内,这棵线段树,当前节点左儿子管的数量 
    if(k<=g)return find(tr[x].lc,tr[y].lc,l,mid,k);
    //k如果小于,自然去左边找 
    else return find(tr[x].rc,tr[y].rc,mid+1,r,k-g);
    //不然去右边找,记得要找k减去g后的排名才是在右儿子中要找的 
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        b[i]=a[i];
    }
    dfs(1,n);
    //b数组排了序!a没有
    //b是下面格子的值,a不变,按a数组的顺序插入 
    for(int i=1;i<=n;i++)
    {
        update(root[i],1,n,seat(a[i]));
        //每次插入以root[i]为根一条链,这条链维护i这个点的信息
        mix(root[i],root[i-1]);
        //将其与前i-1条链合并,这样root[i]为根的这条链维护的就是1~i的信息
    }
    for(int i=1;i<=m;i++)
    {
        int x,y,k;
        scanf("%d %d %d",&x,&y,&k);
        printf("%d\n",find(root[y],root[x-1],1,n,k));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42142540/article/details/80523208