1. 程式人生 > 其它 >洛谷 P2824 [HEOI2016/TJOI2016]排序

洛谷 P2824 [HEOI2016/TJOI2016]排序

Description

洛谷傳送門

Solution

一眼就能想到線段樹,但是線段樹似乎也無法維護啊?那怎麼做?

於是我們檢視標籤……

發現……二分答案?這怎麼二分答案??

於是我們再回想一下題目要求計算什麼:位置是 \(q\) 的數是多少。

而且只有一次詢問。

既然是二分答案,那我們就二分唄。

假設答案是 \(mid\),那現在的問題就是判斷 \(mid\) 是不是操作完之後是第 \(q\) 個數。

考慮到線段樹只能進行區間維護,比如區間加,區間修改,區間求和什麼的……

誒,等等,如果我們把小於 \(mid\) 的數都重新賦值為 0,大於等於 \(mid\) 的數都賦值為 1,那這個排序是不是就相當於區間賦值了呢?對於從小到大排序,我們不需要知道數到底是多少,只需要把 0 全都放到區間的前半段,1 放到後半段即可。反之同理。

操作完之後,查詢第 \(q\) 位是 0 還是 1,如果是 0,說明這個 \(mid\) 太大了,需要調小,反之則需要調大。

這樣一來,這道題就可以完美的解決了,時間複雜度 \(O(nlog^2_n)\)

最後再講一下如何區間修改:我們記錄一下區間和,那麼這個區間和就是區間裡 1 的個數,每次修改時先查詢一下區間和,然後覆蓋即可,具體看程式碼吧。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ls rt << 1
#define rs rt << 1 | 1

using namespace std;

inline int read(){
	int x = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}

const int N = 1e5 + 10;
int n, m, maxs, p, ans;
int a[N], sum[N << 2], lazy[N << 2];
bool b[N];
struct node{
	int op, l, r;
}q[N];

inline void pushup(int rt){
	sum[rt] = sum[ls] + sum[rs];
}

inline void pushdown(int l, int r, int rt){
	if(lazy[rt] != -1){
		int mid = (l + r) >> 1;
		sum[ls] = lazy[rt] * (mid - l + 1);
		sum[rs] = lazy[rt] * (r - mid);
		lazy[ls] = lazy[rs] = lazy[rt];
		lazy[rt] = -1;
	}
}

inline void build(int l, int r, int rt){
	lazy[rt] = -1;
	if(l == r){
		sum[rt] = b[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, ls);
	build(mid + 1, r, rs);
	pushup(rt);
}

inline int query(int L, int R, int l, int r, int rt){
	if(L <= l && r <= R) return sum[rt];
	pushdown(l, r, rt);
	int mid = (l + r) >> 1;
	int res = 0;
	if(L <= mid) res += query(L, R, l, mid, ls);
	if(R > mid) res += query(L, R, mid + 1, r, rs);
	pushup(rt);
	return res;
}

inline void update(int L, int R, int k, int l, int r, int rt){
	if(L <= l && r <= R){
		sum[rt] = k * (r - l + 1);
		lazy[rt] = k;
		return;
	}
	pushdown(l, r, rt);
	int mid = (l + r) >> 1;
	if(L <= mid) update(L, R, k, l, mid, ls);
	if(R > mid) update(L, R, k, mid + 1, r, rs);
	pushup(rt);
}

inline bool check(int mid){
	for(int i = 1; i <= n; i++)
		b[i] = a[i] >= mid;
	build(1, n, 1);
	for(int i = 1; i <= m; i++){
		int s = query(q[i].l, q[i].r, 1, n, 1);
		if(!s || s == q[i].r - q[i].l + 1) continue;
		if(!q[i].op) update(q[i].l, q[i].r - s, 0, 1, n, 1), update(q[i].r - s + 1, q[i].r, 1, 1, n, 1);
		else update(q[i].l, q[i].l + s - 1, 1, 1, n, 1), update(q[i].l + s, q[i].r, 0, 1, n, 1);
	}
	return query(p, p, 1, n, 1);
}

int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i++)
		a[i] = read();
	for(int i = 1; i <= m; i++)
		q[i].op = read(), q[i].l = read(), q[i].r = read();
	p = read();
	int l = 1, r = n;
	while(l <= r){
		int mid = (l + r) >> 1;
		if(check(mid)) ans = mid, l = mid + 1;
		else r = mid - 1;
	}
	printf("%d\n", ans);
	return 0;
}

End

本文來自部落格園,作者:{xixike},轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15407265.html