[LOJ#2585][APIO2018]新家(扫描线 + 二分 + 线段树)

Address

Solution

  • 自从来到 GZ 市来一直都鸽着没去调这题,几天之后才去调

最直接的 O ( ( n + q ) log ⁡ 3 n ) O((n+q)\log^3n) O((n+q)log3n) 思路

  • 看到我们的询问要求「最小值的最大值」,容易想到二分答案
  • 具体地,假设我们在某个时间点,知道了这时候所有点的出现位置,那么二分答案 m i d mid mid 之后转化为判定性问题:区间 [ x − m i d , x + m i d ] [x-mid,x+mid] [xmid,x+mid] 内所有点的颜色种类数是否为 k k k
  • 当然,我们的关键是如何知道这时候所有点的出现位置
  • 考虑把一个点的信息用两次事件描述
  • (1)第 i i i 个点的颜色为 t i t_i ti ,在 l i l_i li 时刻于位置 x i x_i xi 出现
  • (2)第 i i i 个点在 r i + 1 r_i+1 ri+1 时刻消失
  • 这样一个点可以拆成两个四元组: ( x i , t i , l i , 1 ) (x_i,t_i,l_i,1) (xi,ti,li,1) ( x i , t i , r i + 1 , − 1 ) (x_i,t_i,r_i+1,-1) (xi,ti,ri+1,1)
  • (第四元为 1 1 1 表示出现,否则表示消失)
  • 把所有的四元组按照第三元进行排序,询问也按照时间离线排序,使用扫描线从左到右扫时间轴,我们的问题转化成:带插入和删除点,求区间内出现的颜色种数
  • 如果是静态的求区间颜色数,那么我们可以对每个位置维护一个 p r e [ i ] pre[i] pre[i] ,表示 i i i 的左边,离 i i i 最近的,与 i i i 同色的位置
  • 这样求 [ l , r ] [l,r] [l,r] 的区间颜色数,就转化成区间 [ l , r ] [l,r] [l,r] 内有多少个 p r e &lt; l pre&lt;l pre<l ,可以使用主席树解决
  • 而此题需要支持插入和删除点,但我们很容易发现插入和删除点影响到的 p r e pre pre 值个数最多为 2 2 2 (插入 / 删除的点本身以及这个点的后继点)
  • 我们可以想到对于每一种颜色维护一个以关键字为坐标位置的 set ,插入一种颜色时在 set 上处理下 p r e pre pre 值的变化之后,使用树状数组套权值线段树来维护 p r e pre pre 的值分布
  • 复杂度 O ( ( n + q ) log ⁡ 3 n ) O((n+q)\log^3n) O((n+q)log3n) (二分一个 log ⁡ \log log ,树状数组套权值线段树两个 log ⁡ \log log

优化

  • 冷静一下!!!!! O ( ( n + q ) log ⁡ 3 n ) O((n+q)\log^3n) O((n+q)log3n) 的复杂度在这题里显然是过不去的
  • 冷静一下!!!!!我们只需要知道区间内是否出现所有 k k k 种颜色,而不要求区间内的颜色种数
  • 首先,各凭本事使用一些奇技淫巧可以求出一个 m a x l maxl maxl ,表示出现所有 k k k 种颜色的所有区间的左端点最大值。可以发现它其实就是所有颜色出现的最右位置的最小值,用 set 维护即可
  • 特殊地,如果某种颜色没出现过则 m a x l = 0 maxl=0 maxl=0
  • 然后我们思考能不能给定一个 r r r ,求出一个最大的 l l l ,使得右端点为 r r r ,左端点为 l l l 的所有区间都包含所有 k k k 种颜色
  • 显然这个 l l l 必须满足 l ≤ m a x l l\le maxl lmaxl
  • 而在 l ≤ m a x l l\le maxl lmaxl 的限制下,区间 [ l , + ∞ ) [l,+\infty) [l,+) 一定出现了所有颜色
  • 我们接着考虑 [ l , r ] [l,r] [l,r] 出现所有颜色的条件,很容易发现这个条件是:对于任意的 i ∈ ( r , + ∞ ) i\in(r,+\infty) i(r,+) 都必须有 p r e i ≥ l pre_i\ge l preil
  • 于是我们得到
  • l = min ⁡ ( m a x l , min ⁡ i &gt; r p r e i ) l=\min(maxl,\min_{i&gt;r}pre_i) l=min(maxl,i>rminprei)
  • 同时注意判断无解的条件为 l = 0 l=0 l=0
  • 回到原问题,显然二分答案 m i d mid mid 之后 [ x − m i d , x + m i d ] [x-mid,x+mid] [xmid,x+mid] 出现所有 k k k 种颜色的条件为
  • x − m i d ≤ min ⁡ ( m a x l , min ⁡ i &gt; x + m i d p r e i ) x-mid\le\min(maxl,\min_{i&gt;x+mid}pre_i) xmidmin(maxl,i>x+midminprei)
  • 注意离散化对坐标实际值的影响
  • 这时候我们已经将复杂度去掉了一个 log ⁡ n \log n logn ,足以通过此题
  • 当然我们还可以把这个二分答案改成在维护 p r e pre pre 最小值的线段树上二分,做到 O ( ( n + q ) log ⁡ n ) O((n+q)\log n) O((n+q)logn) 的优秀复杂度
  • 此外,由于此题有重复坐标,所以还是有一定细节量的,注意 set 的关键字

Code

#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

typedef std::multiset<int>::iterator its;

template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}

template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}

const int N = 3e5 + 5, M = N << 1, L = M << 2, INF = 0x3f3f3f3f;

int n, k, q, m, real[M], minpre[L], ans[N], lst_col[N];

struct modify
{
	int tm, pos, col, id;
} md[M];

inline bool comp(modify a, modify b)
{
	return a.tm < b.tm;
}

struct query
{
	int tm, pos, id;
} qr[N];

inline bool pmoc(query a, query b)
{
	return a.tm < b.tm;
}

struct elem
{
	int pos, id;
	
	friend inline bool operator < (elem a, elem b)
	{
		return a.pos < b.pos || (a.pos == b.pos && a.id < b.id);
	}
};

std::set<elem> cols[N];
std::multiset<int> pre[M], col_lst;

typedef std::set<elem>::iterator it;

void del_val(int pos, int val)
{
	its pyz = pre[pos].find(val);
	pre[pos].erase(pyz);
}

void change(int l, int r, int pos, int v, int p)
{
	if (l == r) return (void) (minpre[p] = v);
	int mid = l + r >> 1;
	if (pos <= mid) change(l, mid, pos, v, p2);
	else change(mid + 1, r, pos, v, p3);
	minpre[p] = Min(minpre[p2], minpre[p3]);
}

int ask(int l, int r, int x, int ri, int p)
{
	if (l == r) return ri && real[ri] + real[l]
		>= (x << 1) ? l : l + 1;
	int mid = l + r >> 1;
	int delta = real[Min(ri, minpre[p3])] + real[mid];
	if (Min(ri, minpre[p3]) && delta >= (x << 1))
		return ask(l, mid, x, Min(ri, minpre[p3]), p2);
	else return ask(mid + 1, r, x, ri, p3);
}

int __querymin(int l, int r, int s, int e, int p)
{
	if (l == s && r == e) return minpre[p];
	int mid = l + r >> 1;
	if (e <= mid) return __querymin(l, mid, s, e, p2);
	else if (s >= mid + 1) return __querymin(mid + 1, r, s, e, p3);
	else return Min(__querymin(l, mid, s, mid, p2),
		__querymin(mid + 1, r, mid + 1, e, p3));
}

void ins(int col, int pos, int id)
{
	it pyz = cols[col].lower_bound((elem) {pos, id}), zzq = pyz;
	int lst = zzq == cols[col].begin() ? 0 : (*--zzq).pos;
	if (pyz != cols[col].end())
	{
		del_val(pyz->pos, lst);
		pre[pyz->pos].insert(pos);
		change(1, m, pyz->pos, pre[pyz->pos].empty() ? INF :
			*pre[pyz->pos].begin(), 1);
	}
	pre[pos].insert(lst);
	change(1, m, pos, pre[pos].empty() ? INF :
		*pre[pos].begin(), 1);
	cols[col].insert((elem) {pos, id});
	its ysy = col_lst.find(lst_col[col]);
	col_lst.erase(ysy);
	int n_ysy = cols[col].empty() ? 0 :
		(pyz = cols[col].end(), *--pyz).pos;
	col_lst.insert(lst_col[col] = n_ysy);
}

void del(int col, int pos, int id)
{
	it pyz = cols[col].find((elem) {pos, id}), zzq = pyz, zzt = pyz;
	int lst = zzq == cols[col].begin() ? 0 : (*--zzq).pos;
	del_val(pos, lst);
	change(1, m, pos, pre[pos].empty() ? INF :
		*pre[pos].begin(), 1);
	if ((++zzt) != cols[col].end())
	{
		del_val(zzt->pos, pyz->pos);
		pre[zzt->pos].insert(lst);
		change(1, m, zzt->pos, pre[zzt->pos].empty() ? INF :
			*pre[zzt->pos].begin(), 1);
	}
	cols[col].erase(pyz);
	its ysy = col_lst.find(lst_col[col]);
	col_lst.erase(ysy);
	int n_ysy = cols[col].empty() ? 0 :
		(pyz = cols[col].end(), *--pyz).pos;
	col_lst.insert(lst_col[col] = n_ysy);
}

int querymin()
{
	return *col_lst.begin();
}

int main()
{
	memset(minpre, INF, sizeof(minpre));
	int x, t, l, r, p = 1;
	n = read(); k = read(); q = read();
	for (int i = 1; i <= k; i++) col_lst.insert(lst_col[i] = 0);
	for (int i = 1; i <= n; i++)
	{
		x = read(); t = read(); l = read(); r = read();
		md[(i << 1) - 1] = (modify) {l, x, t, i};
		md[i << 1] = (modify) {r + 1, x, t, -i};
		real[++m] = x;
	}
	for (int i = 1; i <= q; i++)
		x = read(), t = read(), qr[i] = (query) {t, x, i},
		real[++m] = x;
	std::sort(md + 1, md + (n << 1) + 1, comp);
	std::sort(qr + 1, qr + q + 1, pmoc);
	std::sort(real + 1, real + m + 1);
	int tm = std::unique(real + 1, real + m + 1) - real - 1;
	m = tm;
	for (int i = 1; i <= (n << 1); i++)
		md[i].pos = std::lower_bound
			(real + 1, real + m + 1, md[i].pos) - real;
	for (int i = 1; i <= q; i++)
		qr[i].pos = std::lower_bound
			(real + 1, real + m + 1, qr[i].pos) - real;
	for (int i = 1; i <= q; i++)
	{
		while (p <= (n << 1) && md[p].tm <= qr[i].tm)
		{
			if (md[p].id > 0) ins(md[p].col, md[p].pos, md[p].id);
			else del(md[p].col, md[p].pos, -md[p].id);
			p++;
		}
		int tmp = querymin();
		if (!tmp)
		{
			ans[qr[i].id] = -1; continue;
		}
		int t = ask(1, m, real[qr[i].pos], tmp, 1);
		ans[qr[i].id] = t == m + 1 ? INF : real[t] - real[qr[i].pos];
		if (t > 1)
		{
			int fstqwq = t == m + 1 ? tmp :
				Min(tmp, __querymin(1, m, t, m, 1));
			if (!fstqwq) continue;
			ans[qr[i].id] = Min(ans[qr[i].id],
				real[qr[i].pos] - real[fstqwq]);
		}
	}
	for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值