【玄3关】刚学线段树,0pts,求调
查看原帖
【玄3关】刚学线段树,0pts,求调
726525
CuteChat楼主2024/3/4 17:08
#include <bits/stdc++.h>
using namespace std;
#define int long long

int n, Q, a[200001], ans[200001], rem[200001];
map<int, vector<int>> pos;
struct Query {
	int l, r, x, id;
} q[200001];
map<int, vector<Query>> qs;

struct Info {
	int tot, lh, rh; // 最左边有没有块,最右边有没有块
};
Info add(const Info a, const Info b)  { // 注意该运算不满足交换律
	if (a.tot + a.lh + a.rh == -3) return b;
	if (b.tot + b.lh + b.rh == -3) return a;
	return {a.tot + b.tot - (a.rh && b.lh), a.lh, b.rh};
}

namespace SGT {
	Info res[2000003];
#define ls(i) (i << 1)
#define rs(i) (i << 1 | 1)
	void build(int i, int l, int r) {
		if (l == r) return res[i] = {1, 1, 1}, void();
		int mid = (l + r) / 2; build(ls(i), l, mid); build(rs(i), mid + 1, r);
		res[i] = add(res[ls(i)], res[rs(i)]);
	}
	void update(int i, int l, int r, int qp) {
		if (l > qp || r < qp) return; int mid = (l + r) / 2;
		if (l == r && l == qp) return res[i] = {0, 0, 0}, void();
		update(ls(i), l, mid, qp); update(rs(i), mid + 1, r, qp);
		res[i] = add(res[ls(i)], res[rs(i)]);
	}
	Info query(int i, int l, int r, int ql, int qr) {
		if (l > qr || r < ql) return {-1, -1, -1};
		//printf("%d %d %d %d %d\n", l, r, res[i].tot, res[i].lh, res[i].rh);
		if (l >= ql && r <= qr) return res[i];
		int mid = (l + r) / 2; 
		return add(query(ls(i), l, mid ,ql, qr), query(rs(i), mid + 1, r, ql, qr));
	}
}

signed main() {
	scanf("%lld%lld", &n, &Q);
	vector<int> qss;
	for (int i = 1; i <= n; ++i) {
		scanf("%lld", &a[i]);
		pos[a[i]].push_back(i);
		qss.push_back(a[i]);
	}
	sort(qss.begin(), qss.end());
	auto enit = unique(qss.begin(), qss.end());
	int minv = 1ll << 60;
	for (int i = 1; i <= Q; ++i) {
		scanf("%lld%lld%lld", &q[i].l, &q[i].r, &q[i].x);
		if (q[i].x < qss[0]) q[i].x = -1;
		else q[i].x = qss[min((std::vector<long long>::size_type)(lower_bound(qss.begin(), enit, q[i].x + 1) - qss.begin() - 1), qss.size() - 1)];
		q[i].id = i; qs[q[i].x].push_back(q[i]);
		minv = min(minv, q[i].x);
	}
	for (int i = 1; i <= n; ++i) {
		if (a[i] < minv) pos[minv].push_back(i);
	}
	SGT::build(1, 1, n);
	for (auto i : qs) {
		for (auto j : pos[i.first]) {
			if (rem[j]) continue;
			rem[j] = 1;
			SGT::update(1, 1, n, j);
		}
		for (auto j : i.second) {
			Info tmp = SGT::query(1, 1, n, j.l, j.r);
			ans[j.id] = tmp.tot;
			//printf("ans: %lld, %lld, %lld, %lld\n", j.id, tmp.tot, tmp.lh, tmp.rh);
		}
	}
	for (int i = 1; i <= Q; ++i) printf("%lld\n", ans[i]);
}


大体思路:

  1. 首先所有的询问的 xx 参数,把 xx 重新赋值为在 aa 序列中比他小于等于的最大值,如果不存在就改成 00。
  2. 然后按照询问参数 xx 从小到大模拟涨水,对于每一次涨水,看看哪些点被淹没了,在线段树中作出对应的修改。
  3. 最后查询区间,还原询问。

线段树记录的节点表示目前的岛屿组成极大未淹没连续子段的情况,tot 表示总数,lh 表示这个区间最左边的岛屿是否被淹没,rh 表示这个区间最右边的岛屿是否被淹没。

严重怀疑是第一步写错了,或者是线段树 query 返回 {-1, -1, -1} 这个地方。

2024/3/4 17:08
加载中...