P2824 [HEOI2016/TJOI2016] 排序

思路

你很快能想到 ODT,然后,你会想到要维护一个存在的系统,那么你能想到平衡树,但是由于 FHQ 不支持乱序合并,平衡树用来解决区间问题,天然有左小右大,所以我们考虑用可莉线段树。

#include <iostream>
#include <map>

using namespace std;

const int MaxN = 1e5 + 10; 

struct S {
	int x, ls, rs;
} d[MaxN << 5];

map<int, int> st;
int ol[MaxN << 5], rt[MaxN], a[MaxN], tot, n, m, p;

int erase(int x) {
	return ol[++ol[0]] = x, d[x].x = d[x].ls = d[x].rs = 0;
}

int newnode() {
	return ol[0] ? ol[ol[0]--] : ++tot;
}

void modify(int &x, int k, int w, int l = 0, int r = n) {
	x || (x = newnode()), d[x].x += w;
	if (l == r) return;
	int mid = l + r >> 1;
	if (k <= mid) modify(d[x].ls, k, w, l, mid);
	if (k > mid) modify(d[x].rs, k, w, mid + 1, r);
}

int merge(int x, int y) {
	if (!x || !y) return x | y;
	d[x].x += d[y].x, d[x].ls = merge(d[x].ls, d[y].ls), d[x].rs = merge(d[x].rs, d[y].rs);
	return erase(y), x; 
}

void split(int x, int &y, int k) {
	if (!x) return;
	y = newnode();
	if (d[d[x].ls].x < k) split(d[x].rs, d[y].rs, k - d[d[x].ls].x);
	else swap(d[x].rs, d[y].rs);
	if (d[d[x].ls].x > k) split(d[x].ls, d[y].ls, k);
	d[y].x = d[x].x - k, d[x].x = k; 
}

int length(auto it) {
	return (next(it) == st.end() ? n + 1 : next(it)->first) - it->first;
}

int kth(int x, int k, int l = 0, int r = n) {
	if (!x) return -1;
	if (l == r) return l;
	int mid = l + r >> 1;
	if (d[d[x].ls].x < k) return kth(d[x].rs, k - d[d[x].ls].x, mid + 1, r);
	return kth(d[x].ls, k, l, mid);
}

int to(int k) {
	auto it = prev(st.upper_bound(k));
	if (it->second) {
		return kth(rt[it->first], length(it) - (k - it->first + 1) + 1);
	}
	return kth(rt[it->first], k - it->first + 1);
} 

void split(int p) {
	auto it = prev(st.upper_bound(p));
	if (it->first == p) return;
	if (it->second) {
		swap(rt[it->first], rt[p]), split(rt[p], rt[it->first], length(it) - (p - it->first));
	} else {
		split(rt[it->first], rt[p], p - it->first);
	}
	st.insert(it, {p, it->second});
}

void assign(int l, int r, int c) {
	split(r + 1), split(l);
	for (auto it = st.find(l); it->first != r + 1; it = st.erase(it)) {
		if (it->first != l) rt[l] = merge(rt[l], rt[it->first]);
	}
	st[l] = c;
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i], st[i] = 0, modify(rt[i], a[i], 1);
	}
	for (int i = 1, l, r, op; i <= m; i++) {
		cin >> op >> l >> r;
		assign(l, r, op);
	}
	cin >> p;
	cout << to(p) << '\n';
	return 0;
}

跑的飞快 提交记录,直接占第四版,我还没卡常

另一种思路,考虑当一个序列有序时,将小于 val 的设为 0,否则为 1 若 pos 的值为 1,那么这个 val 还可以变大,所以我们考虑二分,对于操作我们发现已经变成了 01 序列,所以很容易进行一个相对的排序,具体的,可以通过区间修改 0/1 来实现

posted @ 2025-10-08 00:58  yabnto  阅读(7)  评论(0)    收藏  举报