【JZOJ 4605】【BZOJ 4552】排序

本文介绍了一种使用线段树和二分查找解决序列局部排序后查询特定位置元素的算法。通过将序列中大于中间值的元素标记为1,其余标记为0,并利用线段树维护区间内的标记状态,实现快速查询和更新。

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

Description

在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题
,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排
序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序最后询问第q
位置上的数字。

Solution

二分答案,大于mid的数改成1,小于等于的改成0,
于是每次的改变就变成了用线段树来维护:查询当前区间的1的个数,
最后查询一下那一位,就可以确认往左还是往右

Code

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define merge(e,q,w) b[e]=b[q]+b[w]
using namespace std;
const int N=100500,maxlongint=2107483647;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n,m,m1,ans,a[N];
int b[3*N],la[N*3];
int sc[N][3];
void build(int l,int r,int e,int l1)
{
    if(l==r){b[e]=(a[l]>l1);return;}
    int t=(l+r)/2;
    build(l,t,e*2,l1),build(t+1,r,e*2+1,l1);
    merge(e,e*2,e*2+1);
}
void doit(int l,int r,int e)
{
    if(la[e]==-1)return;
    b[e]=la[e]*(r-l+1);
    if(l!=r)la[e*2]=la[e],la[e*2+1]=la[e];
    la[e]=-1;
}
int find(int l,int r,int e,int l1,int r1)
{
    doit(l,r,e);
    if(l==l1&&r==r1)return b[e];
    int t=(l+r)/2;
    if(r1<=t)return find(l,t,e*2,l1,r1);
        else if(t<l1)return find(t+1,r,e*2+1,l1,r1);
            else return find(l,t,e*2,l1,t)+find(t+1,r,e*2+1,t+1,r1);
}
void change(int l,int r,int e,int l1,int r1,int l2)
{
    if(l1>r1)return;
    doit(l,r,e);
    if(l==l1&&r==r1){la[e]=l2;doit(l,r,e);return;}
    int t=(l+r)/2;
    if(r1<=t)change(l,t,e*2,l1,r1,l2),doit(t+1,r,e*2+1);
        else if(t<l1)doit(l,t,e*2),change(t+1,r,e*2+1,l1,r1,l2);
            else change(l,t,e*2,l1,t,l2),change(t+1,r,e*2+1,t+1,r1,l2);
    merge(e,e*2,e*2+1);
}
int main()
{
    int q,w,e;
    scanf("%d%d",&n,&m);
    fo(i,1,n)read(a[i]);
    fo(i,1,m)fo(j,0,2)read(sc[i][j]);
    read(m1);
    int l=1,r=n;
    while(l<r)
    {
        int t=(l+r)/2;
        memset(la,255,sizeof(la));
        build(1,n,1,t);
        fo(i,1,m)
        {   
            q=find(1,n,1,sc[i][1],sc[i][2]);
            if(!sc[i][0])
            {
                change(1,n,1,sc[i][1],sc[i][2]-q,0);
                change(1,n,1,sc[i][2]-q+1,sc[i][2],1);
            }else
            {
                change(1,n,1,sc[i][1],sc[i][1]-1+q,1);
                change(1,n,1,sc[i][1]+q,sc[i][2],0);
            }
        }
        if(find(1,n,1,m1,m1))l=t+1;
            else r=t;
    }
    printf("%d\n",l);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值