【LOJ2585】「APIO2018」新家

【题目链接】

【思路要点】

  • 对时间轴进行扫描线,在时间轴上的一段区间可以表示为一次插入操作和一次删除操作。
  • 问题被转化为:维护一个序列,支持在某处插入/删除一个数,以及询问以某个位置为中心,包含所有种类的数的区间的最小长度。
  • 对于询问,不难想到二分答案,二分答案后我们需要支持的是询问区间内是否出现了所有种类的数。
  • 对于每一种数,我们维护一个支持查询前驱后继的平衡树(std::set)。
  • 在序列上维护每个位置所有数中,该数上一次出现的位置,并取最小值作为这个位置的权值。
  • 在序列末尾补上所有数各一个,当我们询问\([L,R]\)中是否存在所有数的时候,我们可以询问\([R+1,+\infty)\)内的权值最小值是否不小于\(L\)。
  • 离散化商店的坐标,我们可以用线段树+堆来维护上述序列,其修改、查询的时间复杂度均为\(O(LogN)\)。
  • 连上二分的复杂度,总时间复杂度\(O(NLogN+KLogN+QLog^2N)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 6e5 + 5;
const int MAXV = 5e8 + 5;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
	x *= f;
}
struct Heap {
	priority_queue <int, vector<int>, greater<int> > Heap, Delt;
	void push(int x) {Heap.push(x); }
	void delt(int x) {Delt.push(x); }
	int query() {
		while (!Delt.empty() && Heap.top() == Delt.top()) {
			Heap.pop();
			Delt.pop();
		}
		if (Heap.empty()) return MAXV;
		else return Heap.top();
	}
};
struct SegmentTree {
	struct Node {
		int lc, rc;
		int Min;
	} a[MAXN * 2];
	Heap heap[MAXN];
	int root, size, n;
	void build(int &root, int l, int r) {
		root = ++size;
		a[root].Min = MAXV;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	void update(int root) {
		a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min);
	}
	void insert(int root, int l, int r, int pos, int val) {
		if (l == r) {
			heap[l].push(val);
			a[root].Min = heap[l].query();
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) insert(a[root].lc, l, mid, pos, val);
		else insert(a[root].rc, mid + 1, r, pos, val);
		update(root);
	}
	void insert(int x, int val) {
		insert(root, 1, n, x, val);
	}
	void delt(int root, int l, int r, int pos, int val) {
		if (l == r) {
			heap[l].delt(val);
			a[root].Min = heap[l].query();
			return;
		}
		int mid = (l + r) / 2;
		if (mid >= pos) delt(a[root].lc, l, mid, pos, val);
		else delt(a[root].rc, mid + 1, r, pos, val);
		update(root);
	}
	void delt(int x, int val) {
		delt(root, 1, n, x, val);
	}
	int query(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].Min;
		int mid = (l + r) / 2, ans = MAXV;
		if (mid >= ql) chkmin(ans, query(a[root].lc, l, mid, ql, min(qr, mid)));
		if (mid + 1 <= qr) chkmin(ans, query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr));
		return ans;
	}
	bool query(int ql, int qr) {
		return query(root, 1, n, qr + 1, n) >= ql;
	}
} ST;
struct changes {int x, y, t; bool ins; } c[MAXN];
struct querys {int x, y, home; } q[MAXN];
int n, tn, k, m, ans[MAXN];
int tot, cnt[MAXN], tpos[MAXN];
multiset <int> st[MAXN];
bool cmpc(changes a, changes b) {return a.y < b.y; }
bool cmpq(querys a, querys b) {return a.y < b.y; }
int low(int x) {
	return upper_bound(tpos + 1, tpos + tn + 1, x) - tpos - 1;
}
int up(int x) {
	return lower_bound(tpos + 1, tpos + tn + 1, x) - tpos;
}
void change(changes now) {
	multiset <int> :: iterator pos, tmp;
	int suc, pre;
	if (now.ins) {
		if (cnt[now.t]++ == 0) tot++;
		pos = st[now.t].insert(now.x);
		if (pos == st[now.t].begin()) pre = 0;
		else {tmp = pos; tmp--; pre = *tmp; }
		tmp = pos; tmp++;
		if (tmp == st[now.t].end()) suc = 0;
		else suc = *tmp;
		suc = low(suc);
		now.x = low(now.x);
		if (pre != 0) pre = low(pre);
		ST.delt(suc, pre);
		ST.insert(suc, now.x);
		ST.insert(now.x, pre);
	} else {
		if (--cnt[now.t] == 0) tot--;
		pos = st[now.t].lower_bound(now.x);
		if (pos == st[now.t].begin()) pre = 0;
		else {tmp = pos; tmp--; pre = *tmp; }
		tmp = pos; tmp++;
		if (tmp == st[now.t].end()) suc = 0;
		else suc = *tmp;
		st[now.t].erase(pos);
		suc = low(suc);
		now.x = low(now.x);
		if (pre != 0) pre = low(pre);
		ST.delt(suc, now.x);
		ST.delt(now.x, pre);
		ST.insert(suc, pre);
	}
}
int query(int x) {
	if (tot != k) return -1;
	int l = 0, r = 1e8;
	while (l < r) {
		int mid = (l + r) / 2;
		int ql = up(x - mid), qr = low(x + mid);
		if (ST.query(ql, qr)) r = mid;
		else l = mid + 1;
	}
	return l;
}
int main() {
	read(n), read(k), read(m);
	for (int i = 1; i <= n; i++) {
		int x, t, a, b;
		read(x), read(t), read(a), read(b);
		c[++tn] = (changes) {x, a, t, true};
		c[++tn] = (changes) {x, b + 1, t, false};
	}
	n = tn;
	for (int i = 1; i <= m; i++)
		read(q[i].x), read(q[i].y), q[i].home = i;
	sort(c + 1, c + n + 1, cmpc);
	sort(q + 1, q + m + 1, cmpq);
	tn = 0;
	for (int i = 1; i <= n; i++)
		tpos[++tn] = c[i].x;
	tpos[++tn] = MAXV; 
	sort(tpos + 1, tpos + tn + 1);
	tn = unique(tpos + 1, tpos + tn + 1) - tpos - 1;
	ST.init(tn);
	for (int i = 1; i <= k; i++) {
		st[i].insert(MAXV);
		ST.insert(tn, 0);
	}
	int pos = 1;
	for (int i = 1; i <= m; i++) {
		while (pos <= n && c[pos].y <= q[i].y) change(c[pos++]);
		ans[q[i].home] = query(q[i].x);
	}
	for (int i = 1; i <= m; i++)
		printf("%d\n", ans[i]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值