BZOJ4552 [Tjoi2016&Heoi2016]排序
1. Description
在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。这个难题是这样子的:
给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:
1: (0,l,r)表示将区间[l,r]的数字升序排序
2: (1,l,r)表示将区间[l,r]的数字降序排序
最后询问第q位置上的数字。
2. Input
输入数据的第一行为两个整数$n$和$m$。$n$表示序列的长度,$m$表示局部排序的次数。$1\leq n, m\leq10^5$
第二行为$n$个整数,表示1到$n$的一个全排列。
接下来输入$m$行,每一行有三个整数$op, l, r$,$op$为0代表升序排序,$op$为1代表降序排序,$l, r$表示排序的区间。
最后输入一个整数$q$,$q$表示排序完之后询问的位置, $1\leq q\leq n$。$1\leq n\leq10^5$,$1\leq m\leq 10^5$
3. Output
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第$q$位置上的数字。
4. Sample Input
6 3 1 6 2 5 3 4 0 1 4 1 3 6 0 2 4 3
5. Sample Output
5
5. 思路
离线询问之后二分答案。使用线段树维护,将大于等于二分的$mid$值的数视为1,否则为0,这样查询的序列就变成了一个01序列。此时一个区间被排序后会是一段全是0一段全是1的状态,因此排序只需要查询区间的1的个数(这里也就是区间求和)然后进行区间修改就可以了。最后判断一下$q$位置的值是否为1(不然就小于$mid$了),继续二分直到得到答案。
6. 代码
主要来自http://blog.csdn.net/werkeytom_ftd/article/details/51366237
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <sstream> #include <cctype> #include <cmath> #include <algorithm> #define the_best_pony "Rainbow Dash" using namespace std; const int maxn=100000+10; int a[maxn],sum[maxn*5],set[maxn*5],ask[maxn][3]; bool bz[maxn*5]; int i,j,k,l,r,mid,t,n,m,p; void mark(int p,int l,int r,int v){ sum[p]=(v?0:r-l+1); set[p]=v; bz[p]=1; return; } void down(int p,int l,int r){ int mid=(l+r)/2; if(bz[p]){ mark(p*2,l,mid,set[p]); mark(p*2+1,mid+1,r,set[p]); bz[p]=0; } return; } void change(int p,int l,int r,int a,int b,int v){ if(a>b) return; if(l==a&&r==b){ mark(p,l,r,v); return; } down(p,l,r); int mid=(l+r)/2; if(b<=mid) change(p*2,l,mid,a,b,v); else if(a>mid) change(p*2+1,mid+1,r,a,b,v); else change(p*2,l,mid,a,mid,v),change(p*2+1,mid+1,r,mid+1,b,v); sum[p]=sum[p*2]+sum[p*2+1]; return; } int query(int p,int l,int r,int a,int b){ if(l==a&&r==b) return sum[p]; down(p,l,r); int mid=(l+r)/2; if(b<=mid) return query(p*2,l,mid,a,b); else if(a>mid) return query(p*2+1,mid+1,r,a,b); else return query(p*2,l,mid,a,mid)+query(p*2+1,mid+1,r,mid+1,b); } bool check(int x){ for(int i=1;i<=n;i++) if(a[i]<x) change(1,1,n,i,i,0); else change(1,1,n,i,i,1); for(int i=1;i<=m;i++){ t=query(1,1,n,ask[i][1],ask[i][2]); if(ask[i][0]){ change(1,1,n,ask[i][1],ask[i][2]-t,1); change(1,1,n,ask[i][2]-t+1,ask[i][2],0); } else{ change(1,1,n,ask[i][1],ask[i][1]+t-1,0); change(1,1,n,ask[i][1]+t,ask[i][2],1); } } return !query(1,1,n,p,p); } 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",&ask[i][0],&ask[i][1],&ask[i][2]); scanf("%d",&p); l=1;r=n; while(l<r){ mid=(l+r+1)/2; if(check(mid)) l=mid; else r=mid-1; } printf("%d\n",l); return 0; }