poj 2104

poj2104


今天研究了一下函数式线段树(主席树),感觉很厉害呀!


主席树,其意义是对每序列中每个元素都建一棵子树,来描述前i个元素的信息

直接建树的话,时间、空间都是 NNlogN ,肯定会MLE/TLE

Tree(i) 为描述 1 ~i的线段树

Tree(i) Tree(i1) 会有很多公共的信息,所以只有 logN 条新增信息
所以建树 Tree(i) 时,可以和 Tree(i1) 共用一些节点,其余只需新增 logN 个节点

初始化 Tree(0) :建一棵空树

时间复杂度 O(NlogN)
空间复杂度 O(NlogN)

主席树空间计算: SIZE=NlogN+3N

具体参见fhq神犇的:《范浩强_wc2012谈谈各种数据结构》


对于这个题,我除了写主席树就没动什么脑子,线段树代码直接敲,有些乱搞倾向。。。

tree(i)描述整个序列中 1~i小的元素在树中的分布情况

然后二分答案, l r ,满足 count[i,j,tree(l)]<k<=count[i,j,tree(r)]

最后结果: ans=value(r) ,其实还是写长了。。。

时间复杂度( NlogN+MlogN )

较简易的代码实现: http://blog.csdn.net/q775968375 Orz


UPD:^_^,然而,最后我发现,我的主席树映射写反了233333,难怪代码这么长。。。


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include<math.h>
#define SIZE 2000000
#define MAXN 100005


int n,m;
struct node{int l,r,c[2],cnt;}mp[SIZE]={0};
int ml=0,root[MAXN]={0};
int a[MAXN]={0},rank[MAXN]={0},v[MAXN]={0},swap;
int ll,rr,ki;


int buildtree0(int l0,int r0)
{
    int ret=++ml,mid=(l0+r0)>>1;

    mp[ret].l=l0,mp[ret].r=r0;

    if(l0==r0)return ret;

    if(l0<=mid)  mp[ret].c[0]=buildtree0(l0,mid);
    if(mid+1<=r0)mp[ret].c[1]=buildtree0(mid+1,r0);

    return ret;
}

int buildtree(int rt,int xi)
{
    int mid=(mp[rt].l+mp[rt].r)>>1;
    int ret=++ml,tag;

    mp[ret].l=mp[rt].l;
    mp[ret].r=mp[rt].r;

    if(mp[rt].l==mp[rt].r)
    {
        mp[ret].c[0]=mp[ret].c[1]=0;
        mp[ret].cnt=1;

        return ret;
    }

    /*
    if(xi<=mid)
    {
        mp[ret].c[0]=buildtree(mp[rt].c[0],xi);
        mp[ret].c[1]=mp[rt].c[1];
    }
    else
    {
        mp[ret].c[0]=mp[rt].c[0];
        mp[ret].c[1]=buildtree(mp[rt].c[1],xi);
    }
    */
    tag=(xi<=mid);

    mp[ret].c[tag]=mp[rt].c[tag];
    mp[ret].c[tag^1]=buildtree(mp[rt].c[tag^1],xi);

    mp[ret].cnt=mp[mp[ret].c[0]].cnt+mp[mp[ret].c[1]].cnt;
    return ret;
}

void sort(int l,int r)
{
    int i=l,j=r;
    int t=a[rank[l+rand()%(r-l+1)]];

    while(i<=j)
    {
        while(a[rank[i]]<t)i++;
        while(a[rank[j]]>t)j--;

        if(i<=j)
        {
            swap=rank[i];
            rank[i]=rank[j];
            rank[j]=swap;
            i++,j--;
        }
    }

    if(l<j)sort(l,j);
    if(i<r)sort(i,r);
}

int count(int s,int l,int r)
{
    int mid=(mp[s].l+mp[s].r)>>1;

    if(mp[s].l==l && mp[s].r==r)return mp[s].cnt;

    if(r<=mid)
    {
        return count(mp[s].c[0],l,r);
    }
    else if(l>=mid+1)
    {
        return count(mp[s].c[1],l,r);
    }
    else
    {
        return count(mp[s].c[0],l,mid)+count(mp[s].c[1],mid+1,r);
    }

}
int getnum();
int main()
{
    int i,l,r,mid;

    #ifndef ONLINE_JUDGE
    freopen("poj2104.in","r",stdin);
    freopen("poj2104.out","w",stdout);
    #endif

    srand(time(NULL));

    n=getnum();m=getnum();


    for(i=1;i<=n;i++)
      rank[i]=i,a[i]=getnum();

    if(n!=1)sort(1,n);  

    for(i=1;i<=n;i++)
    {
        v[rank[i]]=i;
    }

    root[0]=buildtree0(1,n);  
    for(i=1;i<=n;i++)
    {      
      root[i]=buildtree(root[i-1],rank[i]);
    }


    while(m--)
    {
        ll=getnum();
        rr=getnum();
        ki=getnum();

        l=1;r=n;
        if(count(root[l],ll,rr)>=ki)
           printf("%d\n",a[rank[l]]);
        else
        {
          while(l+1!=r)
          {
            mid=(l+r)>>1;

            if(count(root[mid],ll,rr)>=ki)
                r=mid;
            else
                l=mid;
          }
          printf("%d\n",a[rank[r]]);
        }
    }


    #ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    #endif
    return 0;
} 

char str[20],sl;int gi,gret,cq;
int getnum()
{   
    gret=0;

    scanf("%s",str);
    sl=strlen(str);

    if(str[0]=='-')cq=-1,gi=1;
    else           cq=1,gi=0;

    for(;gi<sl;gi++)
    {
        gret*=10;
        gret+=str[gi]-'0';
    }

    return gret*cq;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值