1. 程式人生 > 實用技巧 >APIO 2018題解

APIO 2018題解

【Problem 1】

Statemant:

problem

Solution:

發現這題有時間和位置兩個維度,如果按位置排序似乎不太好做,於是考慮按時間排序。對於一個點,有效區間為\([a,b]\),可以把它拆成在\(a\)時間加人,在\(b+1\)時刻刪除。問題變成了如何快速找出最小的\(len\)使得\([p-len,p+len]\)中包含所有種類的點。按照區間數顏色的套路,每個點記錄一下往左走最先找到同顏色點的位置,為了方便,可以在最左邊每種顏色各放一個點。於是查詢一個區間的顏色轉化成查詢區間\([l,r]\)中有多少個點的資訊小於\(l\)。根據單調性可以二分這個區間長度。查詢顏色數的本質是二維數點,又因為二分強制線上,用資料結構維護複雜度應該不小於\(\log_2^2n\)

,再算上二分的複雜度大致是\(\mathcal O(n\log_2^3n)\)。考慮我們並不需要具體算出區間的顏色數,只需要知道這個顏色數是否等於顏色總數,於是考慮查詢\([r+1,n]\)的資訊最小值,如果小於\(l\),說明至少存在一個顏色沒有被區間包括,這個查詢可以用資料結構輕鬆實現。如果用線段樹可以線上段樹上做類似於線段樹二分的操作,總時間複雜度可以做到\(\mathcal O(n \log_2 n)\)

Code

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 3.5E5;
const int LIM = 2E8;
int seg[MAXN*20];
int lc[MAXN*20];
int rc[MAXN*20];
int ans[MAXN];
multiset <int> prePos[MAXN*20];
multiset <int> S[MAXN];

struct Node
{
	int tim;
	int pos;
	int opt;
	int kind;
	int id;
	
	Node() { tim = pos = opt = kind = id = 0; }
	
	Node(int _t, int _p, int _o, int _k, int _id)
	{
		tim = _t, pos = _p, opt = _o, kind = _k, id = _id;
	}
	
	inline friend bool operator < (Node a, Node b)
	{
		if (a.tim != b.tim)
			return a.tim < b.tim;
		return a.opt < b.opt;
	}
} ;

Node A[MAXN<<2];
int n, q, k, rt, totNode;
int p, v, t, curNum;

void upData(int& o, int l, int r)
{
	if (!o)
		o = ++totNode;
	
	if (l == r)
	{
		if (t)
			prePos[o].insert(v);
		else
			prePos[o].erase(prePos[o].find(v));
		seg[o] = (prePos[o].empty()) ? LIM : (*prePos[o].begin());
		return ;
	}
	int mid = l + r >> 1;
	if (p <= mid)
		upData(lc[o], l, mid);
	else
		upData(rc[o], mid + 1, r);
	seg[o] = min(seg[lc[o]], seg[rc[o]]);
}

inline int getAns(int pos)
{
	if (curNum < k)
		return -1;
	int l = 1;
	int r = LIM;
	int nowNode = rt;
	int nowMin = LIM;
	int tmpMin = LIM;
	int mid;
	
	while (l < r)
	{
		mid = l + r >> 1;
		tmpMin = min(nowMin, seg[rc[nowNode]]);
		if ((pos > mid) || (tmpMin < pos * 2 - mid))
			l = mid + 1, nowNode = rc[nowNode];
		else
			nowMin = tmpMin, r = mid, nowNode = lc[nowNode];
	}
	return l - pos;
}

int main(void)
{
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	
	cin >> n >> k >> q, seg[0] = LIM;
	for (int i = 1;i <= k; ++i)
		S[i].insert(-LIM), S[i].insert(LIM), p = LIM, v = -LIM, t = 1, upData(rt, 1, LIM);
	
	int m = 0;
	for (int i = 1;i <= n; ++i)
	{
		int a = 0, b = 0, c = 0, d = 0;
		cin >> a >> b >> c >> d;
		A[++m] = Node(c, a, 0, b, 0),
		A[++m] = Node(d + 1, a, 1, b, 0);
	}
	
	for (int i = 1;i <= q; ++i)
	{
		int a = 0, b = 0;
		cin >> a >> b;
		A[++m] = Node(b, a, 2, 0, i);
	}
	
	sort(A + 1, A + 1 + m);
	
	for (int i = 1;i <= m; ++i)
	{
		int o = A[i].opt;
		int pos = A[i].pos;
		int e = A[i].kind;
		
		if (o == 0)
		{
			int limL = *--S[e].lower_bound(pos);
			int limR = *S[e].lower_bound(pos);
			p = limR, v = pos, t = 1, upData(rt, 1, LIM);
			p = limR, v = limL, t = 0, upData(rt, 1, LIM);
			p = pos, v = limL, t = 1, upData(rt, 1, LIM);
			if (S[e].size() == 2)
				++curNum;
			S[e].insert(pos);
		}
		else
			if (o == 1)
			{
				int limL = *--S[e].lower_bound(pos);
				int limR = *++S[e].lower_bound(pos);
				p = limR, v = pos, t = 0, upData(rt, 1, LIM);
				p = limR, v = limL, t = 1, upData(rt, 1, LIM);
				p = pos, v = limL, t = 0, upData(rt, 1, LIM);
				if (S[e].size() == 3)
					--curNum;
				S[e].erase(S[e].find(pos));
			}
			else
				ans[A[i].id] = getAns(pos);
	}
	
	for (int i = 1;i <= q; ++i)
		cout << ans[i] << '\n';
	
	return 0;
}