【APIO2018】新家 - 题解

题目链接

【APIO2018】新家

做法

对于这种区间修改、单点询问的题目,可以将询问离线再扫描线。
考虑对于一个答案 $ ans_i $ 合法,当且仅当 $ (l_i + ans_i, INF) $ 中每种店上一次出现的位置不小于 $ l_i + ans_i $ 。对于每种店都开一个 multiset 即可维护前驱。
这样每一次加入/删除会需要区间修改前驱,代码难度大,考虑 $ (l_i + ans_i, INF) $ 中每种店上一次出现的位置不小于 $ l_i + ans_i $ 的另一种解释,即 $ (l_i + ans_i, INF) $ 每个位置中每种店每个位置上一次出现的位置最小值不小于 $ l_i + ans_i $ ,然后就可以单点修改了。用线段树维护前驱最小值。
询问可以二分答案,更优秀的做法是在线段树上二分。

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef multiset<int>::iterator IT;
const int N = 600010, LG = 20, INF = 1000000000;
int n, k, m, ans[N], clr;
struct Q {
	int opt, pos, type, pri;
	Q(int Opt = 0, int Pos = 0, int Type = 0, int Pri = 0) :
		opt(Opt), pos(Pos), type(Type), pri(Pri) {}
	inline bool operator<(const Q &yy)const {
		return pri == yy.pri ? opt < yy.opt : pri < yy.pri;
	}
}; Q q[N + N]; int len;

multiset<int> pp[N], st[N * LG];
int rt, tot, cnt, lf[N * LG], rf[N * LG], mn[N * LG], id[N * LG];

inline int Max(const int &x, const int &y) { return x > y ? x : y; }
inline int Min(const int &x, const int &y) { return x < y ? x : y; }
void mdy(int &u, int l, int r, int x, int ad, int dl) {
	if(!u) u = ++tot;
	if(l >= r) {
		if(!id[u]) id[u] = ++cnt;
		multiset<int> &s = st[id[u]];
		if(ad) s.insert(ad);
		if(dl) s.erase(s.find(dl));
		mn[u] = s.size() ? *s.begin() : INF; return ;
	}
	int mid = (l + r) >> 1;
	if(x <= mid) mdy(lf[u], l, mid, x, ad, dl);
	else mdy(rf[u], mid + 1, r, x, ad, dl);
	mn[u] = Min(mn[lf[u]], mn[rf[u]]);
}
void add(Q x) {
	multiset<int> &s = pp[x.type];
	IT itr = s.upper_bound(x.pos), itl = itr; --itl;
	mdy(rt, 0, INF, *itr, x.pos, *itl), mdy(rt, 0, INF, x.pos, *itl, 0);
	s.insert(x.pos); if(s.size() == 3) ++clr;
}
void del(Q x) {
	multiset<int> &s = pp[x.type]; s.erase(s.find(x.pos));
	IT itr = s.upper_bound(x.pos), itl = itr; --itl;
	mdy(rt, 0, INF, *itr, *itl, x.pos), mdy(rt, 0, INF, x.pos, 0, *itl);
	if(s.size() == 2) --clr;
}
int ask(Q x) {
	if(clr < k) return -1;
	int l = 0, r = INF, mid, t, tt = INF, u = rt;
	for(; l < r;) {
		mid = (l + r) >> 1, t = Min(tt, mn[rf[u]]);
		if(x.pos <= mid && t + mid >= x.pos * 2)
			tt = t, r = mid, u = lf[u];
		else u = rf[u], l = mid + 1;
	}
	return l - x.pos;
}
int main() {
	scanf("%d%d%d", &n, &k, &m), mn[0] = INF;
	for(int i = 1; i <= k; i++)
		pp[i].insert(-INF), pp[i].insert(INF), mdy(rt, 0, INF, INF, -INF, 0);
	for(int i = 1, x, t, a, b; i <= n; i++) {
		scanf("%d%d%d%d", &x, &t, &a, &b);
		q[++len] = Q(1, x, t, a), q[++len] = Q(2, x, t, b + 1);
	}
	for(int i = 1, x, y; i <= m; i++)
		scanf("%d%d", &x, &y), q[++len] = Q(3, x, i, y);
	sort(q + 1, q + len + 1);
	for(int i = 1; i <= len; i++) {
		if(q[i].opt == 1) add(q[i]);
		else if(q[i].opt == 2) del(q[i]);
		else ans[q[i].type] = ask(q[i]);
	}
	for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);
	return 0;
}
posted @ 2019-05-22 11:15  daniel14311531  阅读(156)  评论(0编辑  收藏  举报