【二分+线段树】BZOJ4552(Tjoi2016&Heoi2016)[排序]题解

本文介绍了一种使用二分法求解特定排列经过多次局部排序后指定位置数值的算法。通过将问题转化为01序列的操作,利用树状数组进行区间覆盖和查询,最终确定目标位置上的数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目概述

给出一个 n 的排列,对其进行 m 次局部排序:将 [L,R] 升序或降序排序。问最后 pos 上的数。

解题报告

又是二分神题Orz,先二分答案 mid ,就可以把序列按照是否大于 mid 变为 01 序列,这样局部排序就变成了 01 个数查询和区间覆盖。如果最后 pos 上是 1 ,说明答案不小于 mid

示例程序

#include<cstdio>
#include<algorithm>
#define fr first
#define sc second
using namespace std;
const int maxn=100000;

int n,m,pos,now,a[maxn+5],sum[(maxn<<2)+5],tag[(maxn<<2)+5];
pair<int, pair<int,int> > q[maxn+5];

#define LS (p<<1)
#define RS (p<<1|1)
#define Pushup(p) sum[p]=sum[LS]+sum[RS]
void Build(int L,int R,int p=1)
{
    int mid=L+(R-L>>1);if (L==R) {sum[p]=a[mid]>=now;return;}
    tag[p]=-1;Build(L,mid,LS);Build(mid+1,R,RS);Pushup(p);
}
inline void Pushdown(int l,int r,int p)
{
    if (tag[p]==-1) return;int now=tag[p],mid=l+(r-l>>1);tag[p]=-1;
    sum[LS]=now*(mid-l+1);tag[LS]=now;sum[RS]=now*(r-mid);tag[RS]=now;
}
void Cover(int L,int R,int k,int l=1,int r=n,int p=1)
{
    if (R<l||r<L) return;if (L<=l&&r<=R) {sum[p]=k*(r-l+1);tag[p]=k;return;}
    Pushdown(l,r,p);int mid=l+(r-l>>1);
    Cover(L,R,k,l,mid,LS);Cover(L,R,k,mid+1,r,RS);Pushup(p);
}
int Ask(int L,int R,int l=1,int r=n,int p=1)
{
    if (R<l||r<L) return 0;if (L<=l&&r<=R) return sum[p];
    Pushdown(l,r,p);int mid=l+(r-l>>1);
    return Ask(L,R,l,mid,LS)+Ask(L,R,mid+1,r,RS);
}
inline bool check()
{
    for (int i=1;i<=m;i++)
    {
        int td=q[i].fr,L=q[i].sc.fr,R=q[i].sc.sc,num=Ask(L,R);
        if (!td) Cover(L,R-num,0),Cover(R-num+1,R,1); else
        Cover(L,L+num-1,1),Cover(L+num,R,0);
    }
    return Ask(pos,pos);
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=m;i++) scanf("%d%d%d",&q[i].fr,&q[i].sc.fr,&q[i].sc.sc);
    int L=1,R=n;scanf("%d",&pos);
    for (now=L+(R-L>>1);L<=R;now=L+(R-L>>1))
        if (Build(1,n),check()) L=now+1; else R=now-1;
    return printf("%d\n",R),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值