P5356 [Ynoi2017] 由乃打扑克

题目链接

思路:
  实现两个操作,区间加法和查询区间第k大。考虑分块的做法,区间加法对整块来说可以直接另开一个数组\(tag\)来对整块进行区间加法,对于零散块就直接暴力的加上。

区间加法
void update(int l, int r, i64 x) {
	if (belong[l] == belong[r]) {
		for (int i = l; i <= r; i++ )
			a[i] += x;
		for (int i = bln[belong[l]], bel = belong[l]; i <= brn[bel]; i++ ) {
			b[i] = a[i];
		}
		std::sort(b + bln[belong[l]], b + brn[belong[l]] + 1);
		return ;
	}
	for (int i = l, bel = belong[l]; i <= brn[bel]; i++ ) {
		a[i] += x;
	}
	for (int i = bln[belong[r]], bel = belong[r]; i <= r; i++ ) {
		a[i] += x;
	}
	for (int i = bln[belong[l]], bel = belong[l]; i <= brn[bel]; i++ ) {
		b[i] = a[i];
	}
	for (int i = bln[belong[r]], bel = belong[r]; i <= brn[bel]; i++ ) {
		b[i] = a[i];
	}
	std::sort(b + bln[belong[l]], b + brn[belong[l]] + 1);
	std::sort(b + bln[belong[r]], b + brn[belong[r]] + 1);

	for (int i = belong[l] + 1; i < belong[r]; i++ ) {
		tag[i] += x;
	}
}	
查询区间第k大,要用二分来快速的查找,我们对查询区间的所有数字进行二分答案,查询此时二分的值是不是第$k$大,如果比$k$大那就重新定上界,如果比$k$小就重新定下界。对于零散块内大于$mid$的值的计算可以直接暴力,对于整块的计算可能不太好处理,所以要预处理出来一个块内排序好了的数组$b$,这样我们对块内的计算就分类讨论

  \(1.\)块内最大的元素小于\(mid\)
  \(2.\)块内最小的元素大于\(mid\)
  \(3.\)块内最大的元素大于\(mid\),块内最小的元素小于\(mid\),对于这种情况我们二分处理。

查询区间第$k$大
i64 check(int l, int r, i64 x) {
	i64 cnt = 0;
	if (belong[l] == belong[r]) {
		for (int i = l; i <= r; i++ ) {
			cnt += (a[i] + tag[belong[i]] <= x);
		}
		return cnt;
	}

	for (int i = belong[l] + 1; i < belong[r]; i++ ) {
		if (b[bln[i]] + tag[i] > x) continue;
		if (b[brn[i]] + tag[i] <= x) {
			cnt += brn[i] - bln[i] + 1;
			continue;
		}
		int ln = bln[i], rn = brn[i];
		while(ln < rn) {
			int mid = (ln + rn + 1) >> 1;
			if (b[mid] + tag[i] <= x) ln = mid;
			else rn = mid - 1;
		}
		if (b[ln] + tag[i] <= x) cnt += ln - bln[i] + 1;
	}

	for (int i = l, bel = belong[l]; i <= brn[bel]; i++ ) {
		if (a[i] + tag[bel] <= x) cnt ++;
	}
	for (int i = bln[belong[r]], bel = belong[r]; i <= r; i++ ) {
		if (a[i] + tag[bel] <= x) cnt ++;
	}

	return cnt;
}

int query(int l, int r, int k) {
	if (k < 1 || k > r - l + 1) return -1;
	int ans = -1;
	i64 ln = MIN(l, r);
	i64 rn = MAX(l, r);

	while(ln <= rn) {
		i64 mid = (ln + rn) >> 1;
		if (check(l, r, mid) < k) ln = mid + 1;
		else rn = mid - 1, ans = mid;
	}
	return ans;
}

全部的代码:

constexpr int N = 1000010;
constexpr int M = 150;

int n, m;
int bln[N], brn[N], belong[N];
i64 tag[N];
i64 a[N], b[N];

void update(int l, int r, i64 x) {
	if (belong[l] == belong[r]) {
		for (int i = l; i <= r; i++ )
			a[i] += x;
		for (int i = bln[belong[l]], bel = belong[l]; i <= brn[bel]; i++ ) {
			b[i] = a[i];
		}
		std::sort(b + bln[belong[l]], b + brn[belong[l]] + 1);
		return ;
	}
	for (int i = l, bel = belong[l]; i <= brn[bel]; i++ ) {
		a[i] += x;
	}
	for (int i = bln[belong[r]], bel = belong[r]; i <= r; i++ ) {
		a[i] += x;
	}
	for (int i = bln[belong[l]], bel = belong[l]; i <= brn[bel]; i++ ) {
		b[i] = a[i];
	}
	for (int i = bln[belong[r]], bel = belong[r]; i <= brn[bel]; i++ ) {
		b[i] = a[i];
	}
	std::sort(b + bln[belong[l]], b + brn[belong[l]] + 1);
	std::sort(b + bln[belong[r]], b + brn[belong[r]] + 1);

	for (int i = belong[l] + 1; i < belong[r]; i++ ) {
		tag[i] += x;
	}
}	

i64 MAX(int l, int r) {
	i64 res = std::numeric_limits<int>::min();
	if (belong[l] == belong[r]) {
		for (int i = l; i <= r; i ++ )
			res = std::max(res, a[i] + tag[belong[l]]);
		return res;
	}

	for (int i = belong[l] + 1; i < belong[r]; i++ ) {
		res = std::max(res, b[brn[i]] + tag[i]);
	}
	for (int i = bln[belong[r]], bel = belong[r]; i <= r; i++ ) {
		res = std::max(res, a[i] + tag[bel]);
	}
	for (int i = l, bel = belong[l]; i <= brn[bel]; i++ ) {
		res = std::max(res, a[i] + tag[bel]);
	}

	return res;
}

i64 MIN(int l, int r) {
	i64 res = std::numeric_limits<int>::max();
	if (belong[l] == belong[r]) {
		for (int i = l; i <= r; i ++ )
			res = std::min(res, a[i] + tag[belong[l]]);
		return res;
	}

	for (int i = belong[l] + 1; i < belong[r]; i++ ) {
		res = std::min(res, b[bln[i]] + tag[i]);
	}
	for (int i = bln[belong[r]], bel = belong[r]; i <= r; i++ ) {
		res = std::min(res, a[i] + tag[bel]);
	}
	for (int i = l, bel = belong[l]; i <= brn[bel]; i++ ) {
		res = std::min(res, a[i] + tag[bel]);
	}

	return res;
}

i64 check(int l, int r, i64 x) {
	i64 cnt = 0;
	if (belong[l] == belong[r]) {
		for (int i = l; i <= r; i++ ) {
			cnt += (a[i] + tag[belong[i]] <= x);
		}
		return cnt;
	}

	for (int i = belong[l] + 1; i < belong[r]; i++ ) {
		if (b[bln[i]] + tag[i] > x) continue;
		if (b[brn[i]] + tag[i] <= x) {
			cnt += brn[i] - bln[i] + 1;
			continue;
		}
		int ln = bln[i], rn = brn[i];
		while(ln < rn) {
			int mid = (ln + rn + 1) >> 1;
			if (b[mid] + tag[i] <= x) ln = mid;
			else rn = mid - 1;
		}
		if (b[ln] + tag[i] <= x) cnt += ln - bln[i] + 1;
	}

	for (int i = l, bel = belong[l]; i <= brn[bel]; i++ ) {
		if (a[i] + tag[bel] <= x) cnt ++;
	}
	for (int i = bln[belong[r]], bel = belong[r]; i <= r; i++ ) {
		if (a[i] + tag[bel] <= x) cnt ++;
	}

	return cnt;
}

int query(int l, int r, int k) {
	if (k < 1 || k > r - l + 1) return -1;
	int ans = -1;
	i64 ln = MIN(l, r);
	i64 rn = MAX(l, r);

	while(ln <= rn) {
		i64 mid = (ln + rn) >> 1;
		if (check(l, r, mid) < k) ln = mid + 1;
		else rn = mid - 1, ans = mid;
	}
	return ans;
}

signed main() {
	std::cin.tie(nullptr)->sync_with_stdio(false);
	std::cin >> n >> m;

	for (int i = 1; i <= n; i ++ ) {
		std::cin >> a[i];
		b[i] = a[i];
		belong[i] = (i - 1) / M + 1;
	}

	int lim = std::ceil(n * 1.0 / M);
	for (int i = 1; i <= lim; i ++ ) {
		bln[i] = (i - 1) * M + 1, brn[i] = i * M;
	}
	brn[lim] = n;

	for (int i = 1; i <= lim; i++ )
		std::sort(b + bln[i], b + brn[i] + 1);

	for (int i = 0; i < m; i++ ) {
		int op, l, r, k;
		std::cin >> op >> l >> r >> k;
		if (op == 1) std::cout << query(l, r, k) << "\n";
		else update(l, r, k);
	}

	return 0 ^ 0;
}
posted @ 2022-08-12 15:07  浅渊  阅读(66)  评论(0)    收藏  举报