在 2016 2016 2016 年,佳媛姐姐喜欢上了数字序列。因而她经常研究关于序列的一些奇奇怪怪的问题,现在她在研究一个难题,需要你来帮助她。
这个难题是这样子的:给出一个 1 1 1 到 n n n 的排列,现在对这个排列序列进行 m m m 次局部排序,排序分为两种:
0 l r
表示将区间 [ l , r ] [l,r] [l,r] 的数字升序排序1 l r
表示将区间 [ l , r ] [l,r] [l,r] 的数字降序排序注意,这里是对下标在区间 [ l , r ] [l,r] [l,r] 内的数排序。
最后询问第 q q q 位置上的数字。
输入数据的第一行为两个整数 n n n 和 m m m, n n n 表示序列的长度, m m m 表示局部排序的次数。
第二行为 n n n 个整数,表示 1 1 1 到 n n n 的一个排列。
接下来输入 m m m 行,每一行有三个整数 op , l , r \text{op},l,r op,l,r, op \text{op} op 为 0 0 0 代表升序排序, op \text{op} op 为 1 1 1 代表降序排序, l , r l,r l,r 表示排序的区间。
最后输入一个整数 q q q,表示排序完之后询问的位置
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第 q q q 位置上的数字。
6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
5
河北省选2016第一天第二题。
对于 30 % 30\% 30% 的数据, n , m ≤ 1000 n,m\leq 1000 n,m≤1000
对于 100 % 100\% 100% 的数据, n , m ≤ 1 0 5 n,m\leq 10^5 n,m≤105, 1 ≤ q ≤ n 1\leq q\leq n 1≤q≤n
排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),总的时间复杂度为 O ( n m l o g m ) O(nmlogm) O(nmlogm) ,会超时。
对于 01序列
的排序(以升序为例)可以简化排序的过程:
01序列
的排序实际上是去区间查询,区间修改:
接下来考虑如何将序列转化为 01序列:
对于猜测答案 x x x,将序列中小于 x x x 的数置 0,大于等于 x x x 的数置 1,即可得到 01序列
答案是否具有单调性:
对 01序列 进行局部排序操作,然后查询 q q q 位置的数:
因此具有单调性,所以可以二分答案。
总的时间复杂度为 O ( m l o g 2 n ) O(mlog^2n) O(mlog2n)
代码中需要注意的是:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
const int maxn = 1e5+10;
const int maxm = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
inline int ls(int x){return x << 1;}
inline int rs(int x){return x << 1 | 1;}
struct Query{
int op, l, r;
}q[maxn];
struct node{
int sum; // 1 的个数
int tag;
}t[maxn << 2];
int n, m, p;
int a[maxn], b[maxn];
void init(int x){
for(int i = 1; i <= n; i++){
if(a[i] < x)
b[i] = 0;
else
b[i] = 1;
}
}
void pushup(int k){
t[k].sum = t[ls(k)].sum + t[rs(k)].sum;
}
void build(int k, int l, int r){
t[k].tag = 0;
if(l == r){
t[k].sum = b[l];
return;
}
int mid = (l+r) >> 1;
build(ls(k), l, mid);
build(rs(k), mid+1, r);
pushup(k);
}
// tag = 0 : 无事发生
// tag = 1 : 全为 0
// tag = 2 : 全为 1
void pushdown(int k, int l, int r){
if(t[k].tag == 0)
return;
if(t[k].tag == 1){
t[ls(k)].sum = 0;
t[rs(k)].sum = 0;
}
else if(t[k].tag == 2){
int mid = (l+r) >> 1;
t[ls(k)].sum = mid - l + 1;
t[rs(k)].sum = r - mid;
}
t[ls(k)].tag = t[rs(k)].tag = t[k].tag;
t[k].tag = 0;
}
void modify(int k, int l, int r, int x, int y, int v){
if(x <= l && y >= r){
t[k].tag = v;
if(v == 1) t[k].sum = 0;
if(v == 2) t[k].sum = r-l+1;
return;
}
pushdown(k, l, r);
int mid = (l+r) >> 1;
if(x <= mid)
modify(ls(k), l, mid, x, y, v);
if(y > mid)
modify(rs(k), mid+1, r, x, y, v);
pushup(k);
}
int querycnt1(int k, int l, int r, int x, int y){
if(x <= l && y >= r)
return t[k].sum;
int mid = (l+r) >> 1;
pushdown(k, l, r);
int res = 0;
if(x <= mid)
res += querycnt1(ls(k), l, mid, x, y);
if(y > mid)
res += querycnt1(rs(k), mid+1, r, x, y);
return res;
}
int querypos(int k, int l, int r, int pos){
if(l == r && l == pos)
return t[k].sum;
pushdown(k, l, r);
int mid = (l+r) >> 1;
int res;
if(pos <= mid)
res = querypos(ls(k), l, mid, pos);
else
res = querypos(rs(k), mid+1, r, pos);
return res;
}
bool check(int x){
init(x);
build(1, 1, n);
for(int i = 1; i <= m; i++){
int cnt1 = querycnt1(1, 1, n, q[i].l, q[i].r);
if(cnt1 == 0 || cnt1 == (q[i].r - q[i].l + 1))
continue;
if(q[i].op == 0){ // 升序
modify(1, 1, n, q[i].l, q[i].r-cnt1, 1);
modify(1, 1, n, q[i].r-cnt1+1, q[i].r, 2);
}
else if(q[i].op == 1){ // 降序
modify(1, 1, n, q[i].l+cnt1, q[i].r, 1);
modify(1, 1, n, q[i].l, cnt1+q[i].l-1, 2);
}
}
int res = querypos(1, 1, n, p);
return res == 1;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> a[i];
for(int i = 1; i <= m; i++)
cin >> q[i].op >> q[i].l >> q[i].r;
cin >> p;
int l = 1, r = n, res;
while(l <= r){
int mid = (l+r) >> 1;
if(check(mid)){
res = mid;
l = mid + 1;
}
else
r = mid - 1;
}
cout << res << endl;
return 0;
}