整体二分求区间第k大模板(POJ 2104)

 

自己的模板,线段树实现的,可以参考一下

算法步骤

 

参考一下文章

http://www.cnblogs.com/dirge/articles/5810855.html

https://www.cnblogs.com/rayrayrainrain/articles/6477611.html

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>

using namespace std;

#define mem(a,val) memset(a,val,sizeof a)
#define fori(l,r) for( int i = l ; i <= r ; i++ )
#define forj(l,r) for( int j = l ; j <= r ; j++ )
#define fork(l,r) for( int k = l ; k <= r ; k++ )
#define inf 0x3f3f3f3f
#define lef rt<<1
#define rig rt<<1|1
#define mid (l+r)>>1
int n,qcnt;
const int maxn = 2e5+5;
struct spe
{
    int type;
    int l,r;    //求区间k大的左右范围
    int k,pos,val;  //第k大,pos为单点修改的位置
};
spe q[maxn];
int ans[maxn];
spe lq[maxn],rq[maxn];

int tree[maxn<<2];

void updatePoint( int l,int r,int rt,int pos,int val )
{
    if( l == r )
    {
        tree[rt] += val;
        return;
    }
    int m = mid;
    if( m >= pos )
        updatePoint(l,m,lef,pos,val);
    else updatePoint(m+1,r,rig,pos,val);
    tree[rt] = tree[lef]+tree[rig];
}
int L,R;
int query( int l,int r,int rt )
{
    if( l >= L && r <= R )
        return tree[rt];
    int ans = 0;
    int m = mid;
    if( m >= L )
        ans += query(l,m,lef);
    if( m < R )
        ans += query(m+1,r,rig);
    return ans;
}
void solve( int head,int tail,int l,int r )
{
    if( head > tail )
        return;
    if( l == r )    //存储答案
    {
        //cout<<l<<" "<<r<<endl;
        fori(head,tail)
            if( q[i].type == 2 )   //如果是询问类型
            {
                ans[ q[i].pos ] = l;
                //cout<<"yes";
            }
        return;
    }
    int m = mid;
    int lcnt = 1,rcnt = 1;
    fori(head,tail)
    {
        if( q[i].type == 1 )    //修改点类型
        {
            if( q[i].val <= m )     //小于m,这个会对后面查询有影响,所以需要更新
            {
                lq[lcnt++] = q[i];
                updatePoint(1,n,1,q[i].pos,1);  //表示这个位置有1个数
            }
            else rq[rcnt++] = q[i];
        }
        else if( q[i].type == 2 )
        {
            L = q[i].l , R = q[i].r;
            int num = query(1,n,1);
            //cout<<" num : "<<num<<endl;
            if( num >= q[i].k )
                lq[lcnt++] = q[i];
            else
            {
                q[i].k -= num;
                rq[rcnt++] = q[i];
            }
        }
    }
    lcnt--; //左闭右闭区间,所以-1
    rcnt--;
    fori(1,lcnt)    //还原之前的更新
    {
        if( lq[i].type == 1 )
            updatePoint(1,n,1,lq[i].pos,-1);
    }
    //更新q
    int start = head-1;
    fori(1,lcnt)
        q[start+i] = lq[i];
    start = head+lcnt-1;
    fori(1,rcnt)
        q[start+i] = rq[i];
    solve(head,head+lcnt-1,l,m);    //递归解决
    solve(head+lcnt,tail,m+1,r);
}
int main()
{
    while( scanf("%d %d",&n,&qcnt) == 2 )
    {
        int ansCnt = qcnt;
        mem(tree,0);
        qcnt += n;
        fori(1,n)
        {
            scanf("%d",&q[i].val);
            q[i].type = 1;
            q[i].pos = i;
        }

        fori(1,ansCnt)
        {
            scanf("%d %d %d",&q[i+n].l,&q[i+n].r,&q[i+n].k);
            q[i+n].type = 2;
            q[i+n].pos = i;   //这里的pos是为了存储答案,放到ans数组
        }
        solve(1,qcnt,-inf,inf);

        fori(1,ansCnt)
            printf("%d\n",ans[i]);
    }
}
/*
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3

*/

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值