bzoj 4552 排序 线段树+二分 解题报告

来源:互联网 发布:cmf和cms 编辑:程序博客网 时间:2024/06/06 17:22

Description

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

Input

输入数据的第一行为两个整数n和m。n表示序列的长度,m表示局部排序的次数。1 <= n, m <= 10^5第二行为n个整
数,表示1到n的一个全排列。接下来输入m行,每一行有三个整数op, l, r, op为0代表升序排序,op为1代表降序
排序, l, r 表示排序的区间。最后输入一个整数q,q表示排序完之后询问的位置, 1 <= q <= n。1 <= n <= 10^5
,1 <= m <= 10^5

Output

输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第q位置上的数字。

Sample Input

6 3

1 6 2 5 3 4

0 1 4

1 3 6

0 2 4

3

Sample Output

5

思路

二分答案,判断排序之后的A[q]与mid的关系,
先把原序列中大于mid的数标记为1,否则标记为0。
对01序列进行排序,可以通过线段树完成计数排序,
需要实现区间求和、区间赋值。
最后如果A[q]=1,说明原序列中A[q]>mid ,
否则原序列中A[q]  mid 。
求最小的mid满足排序后的序列中A[q] = 0。
复杂度O (nlog^2n)。

代码

#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>#include<cmath>#include<vector>#include<queue>using namespace std;const int N=200000+5; int A[N],v[N];struct data{    int op,x,y;}B[N];int n,M,q,np,rt,cnt[2*N],chi[2*N][2],down[2*N];void build(int &now,int L,int R){    now=++np;cnt[now]=down[now]=0;    chi[now][0]=chi[now][1]=0;    if (L==R) {cnt[now]=v[L];return ;}    int mid=(L+R)>>1;    build(chi[now][0],L,mid);    build(chi[now][1],mid+1,R);    cnt[now]=cnt[chi[now][0]]+cnt[chi[now][1]];    return ;}void pushdown(int now,int L,int R){    int mid=(L+R)/2;    if (down[now])    {        down[chi[now][0]]=down[chi[now][1]]=down[now];        cnt[chi[now][0]]=(mid-L+1)*(down[now]-1);        cnt[chi[now][1]]=(R-mid)*(down[now]-1);    }    down[now]=0;    return ;}void update(int now,int L,int R,int x,int y,int v){    if (x<=L&&R<=y) {down[now]=v+1;cnt[now]=v*(R-L+1);return ;}    pushdown(now,L,R);    int mid=(L+R)>>1;    if (x<=mid) update(chi[now][0],L,mid,x,y,v);    if (y>mid) update(chi[now][1],mid+1,R,x,y,v);    cnt[now]=cnt[chi[now][0]]+cnt[chi[now][1]];    return;}int query(int now,int L,int R,int x,int y){    if (x<=L&&R<=y) return cnt[now];    pushdown(now,L,R);    int mid=(L+R)>>1;    int t1=0,t2=0;    if (x<=mid) t1=query(chi[now][0],L,mid,x,y);    if (y>mid) t2=query(chi[now][1],mid+1,R,x,y);    cnt[now]=cnt[chi[now][0]]+cnt[chi[now][1]];    return t1+t2;                                  }bool check(int x){    for (int i=1;i<=n;i++)    v[i]=A[i]<=x? 0:1;     np=0;int ct;    build(rt,1,n);    for (int i=1;i<=M;i++)    {        ct=query(rt,1,n,B[i].x,B[i].y);        if (B[i].op==0)        {            ct=B[i].y-B[i].x+1-ct;            if (ct>0) update(rt,1,n,B[i].x,B[i].x+ct-1,0);            if (ct<=B[i].y-B[i].x) update(rt,1,n,B[i].x+ct,B[i].y,1);        }        else        {            if (ct>0) update(rt,1,n,B[i].x,B[i].x+ct-1,1);            if (ct<=B[i].y-B[i].x) update(rt,1,n,B[i].x+ct,B[i].y,0);        }    }    return query(rt,1,n,q,q)==0;}int main(){    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",&B[i].op,&B[i].x,&B[i].y);    scanf("%d",&q);    int l=1,r=n,mid,ans;    while(l<=r)    {        mid=(l+r)>>1;        if (check(mid)) { r=mid-1;ans=mid;}        else l=mid+1;    }    printf("%d\n",ans);    return 0;}
原创粉丝点击