天天看點

[BZOJ1178][Apio2009]CONVENTION會議中心(倍增 + 貪心 + 線段樹)AddressCodeCode

Address

洛谷 P3626

BZOJ 1178

Code

  • 先離散化區間端點
  • 預處理出對于每個區間 [ l i , r i ] [l_i,r_i] [li​,ri​] ,滿足 r i &lt; l j r_i&lt;l_j ri​<lj​ 且 r j r_j rj​ 最小的區間 [ l j , r j ] [l_j,r_j] [lj​,rj​]
  • 下文稱之為「 i i i 的後繼為 j j j 」
  • 考慮一個小問題
  • 查詢在包含于 [ L , R ] [L,R] [L,R] 的所有區間中,最多能選出多少個互不相交的區間
  • 先找到包含于 [ L , R ] [L,R] [L,R] 且右端點最小的區間 [ l i , r i ] [l_i,r_i] [li​,ri​] (不存在則答案為 0 0 0 )
  • 問題轉化為 i i i 至少跳多少次後繼之後右端點超過 R R R 或者不存在後繼
  • 倍增 O ( n log ⁡ n ) O(n\log n) O(nlogn) 預處理後 O ( log ⁡ n ) O(\log n) O(logn) 查詢
  • 考慮貪心地編号從小往大,考慮每個區間是否可以成為答案
  • 設目前考慮區間 [ l , r ] [l,r] [l,r]
  • 如果 [ l , r ] [l,r] [l,r] 記憶體在一點,已經被之前選出的區間覆寫,則這個區間不能成為答案
  • 否則設 l 0 l_0 l0​ 為滿足 [ l 0 , l ] [l_0,l] [l0​,l] 都沒有被覆寫的最小的 l 0 l_0 l0​
  • r 0 r_0 r0​ 為滿足 [ r , r 0 ] [r,r_0] [r,r0​] 都沒有被覆寫的最大的 r 0 r_0 r0​
  • 設 q u e r y ( l , r ) query(l,r) query(l,r) 表示包含于 [ l , r ] [l,r] [l,r] 的所有區間中最多選出多少個區間互不相交
  • 如果 q u e r y ( l 0 , l − 1 ) + 1 + q u e r y ( r + 1 , r 0 ) ≠ q u e r y ( l 0 , r 0 ) query(l_0,l-1)+1+query(r+1,r_0)\ne query(l_0,r_0) query(l0​,l−1)+1+query(r+1,r0​)̸​=query(l0​,r0​) 則區間 [ l , r ] [l,r] [l,r] 不能成為答案
  • 否則區間 [ l , r ] [l,r] [l,r] 可以成為答案,直接加入答案
  • 複雜度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

Code

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#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;
}

const int N = 2e5 + 5, M = N << 1, L = M << 2, LogN = 20;

int n, m, tm, tmp[M], fa[N][LogN], dep[N], ri[M], sum[L], add[L],
ansm, ans[N], pre0[L], suf0[L];

std::vector<int> itv[M];

struct node
{
	int l, r;
} a[N];

void build(int l, int r, int p)
{
	sum[p] = add[p] = 0;
	pre0[p] = suf0[p] = r - l + 1;
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, p2); build(mid + 1, r, p3);
}

void down(int p)
{
	add[p2] += add[p]; add[p3] += add[p];
	add[p] = 0;
}

void upt(int l, int r, int p)
{
	int mid = l + r >> 1, lp0, rp0, ls0, rs0;
	sum[p] = sum[p2] + sum[p3]
		+ (mid - l + 1) * add[p2] + (r - mid) * add[p3];
	lp0 = add[p2] ? 0 : pre0[p2];
	rp0 = add[p3] ? 0 : pre0[p3];
	ls0 = add[p2] ? 0 : suf0[p2];
	rs0 = add[p3] ? 0 : suf0[p3];
	pre0[p] = lp0 == mid - l + 1 ? lp0 + rp0 : lp0;
	suf0[p] = rs0 == r - mid ? rs0 + ls0 : rs0;
}

void change(int l, int r, int s, int e, int v, int p)
{
	if (l == s && r == e) return (void) (add[p] += v);
	int mid = l + r >> 1;
	down(p);
	if (e <= mid) change(l, mid, s, e, v, p2);
	else if (s >= mid + 1) change(mid + 1, r, s, e, v, p3);
	else change(l, mid, s, mid, v, p2),
		change(mid + 1, r, mid + 1, e, v, p3);
	upt(l, r, p);
}

int ask(int l, int r, int s, int e, int p)
{
	if (l == s && r == e) return sum[p] + (r - l + 1) * add[p];
	int mid = l + r >> 1, res;
	down(p);
	if (e <= mid) res = ask(l, mid, s, e, p2);
	else if (s >= mid + 1) res = ask(mid + 1, r, s, e, p3);
	else res = ask(l, mid, s, mid, p2)
		+ ask(mid + 1, r, mid + 1, e, p3);
	return upt(l, r, p), res;
}

int askpre0(int l, int r, int s, int e, int p)
{
	if (l == s && r == e) return add[p] ? 0 : pre0[p];
	int mid = l + r >> 1, res;
	down(p);
	if (e <= mid) res = askpre0(l, mid, s, e, p2);
	else if (s >= mid + 1) res = askpre0(mid + 1, r, s, e, p3);
	else
	{
		int lp = askpre0(l, mid, s, mid, p2),
			rp = askpre0(mid + 1, r, mid + 1, e, p3);
		res = lp == mid - s + 1 ? lp + rp : lp;
	}
	return upt(l, r, p), res;
}

int asksuf0(int l, int r, int s, int e, int p)
{
	if (l == s && r == e) return add[p] ? 0 : suf0[p];
	int mid = l + r >> 1, res;
	down(p);
	if (e <= mid) res = asksuf0(l, mid, s, e, p2);
	else if (s >= mid + 1) res = asksuf0(mid + 1, r, s, e, p3);
	else
	{
		int ls = asksuf0(l, mid, s, mid, p2),
			rs = asksuf0(mid + 1, r, mid + 1, e, p3);
		res = rs == e - mid ? rs + ls : rs;
	}
	return upt(l, r, p), res;
}

int getans(int l, int r)
{
	if (l > r) return 0;
	if (!ri[l] || a[ri[l]].r > r) return 0;
	int i, u = ri[l], st = u;
	Rof (i, 17, 0)
		if (fa[u][i] && a[fa[u][i]].r <= r)
			u = fa[u][i];
	return dep[st] - dep[u] + 1;
}

int main()
{
	int i, j, k, sz;
	n = read();
	For (i, 1, n)
	{
		a[i].l = read(); a[i].r = read();
		tmp[++tm] = a[i].l;
		tmp[++tm] = a[i].r;
	}
	std::sort(tmp + 1, tmp + tm + 1);
	m = std::unique(tmp + 1, tmp + tm + 1) - tmp - 1;
	For (i, 1, n)
	{
		a[i].l = std::lower_bound(tmp + 1, tmp + m + 1, a[i].l) - tmp;
		a[i].r = std::lower_bound(tmp + 1, tmp + m + 1, a[i].r) - tmp;
	}
	For (i, 1, n) itv[a[i].l].push_back(i);
	For (i, 1, n) if (!ri[a[i].l] || a[i].r < a[ri[a[i].l]].r)
		ri[a[i].l] = i;
	Rof (i, m - 1, 1)
		ri[i] = !ri[i] || !ri[i + 1] ? ri[i] + ri[i + 1] :
			(a[ri[i]].r < a[ri[i + 1]].r ? ri[i] : ri[i + 1]);
	Rof (i, m, 1)
	{
		int sz = itv[i].size();
		For (j, 0, sz - 1)
		{
			int u = itv[i][j];
			fa[u][0] = ri[a[u].r + 1];
			dep[u] = dep[ri[a[u].r + 1]] + 1;
			For (k, 0, 16)
				fa[u][k + 1] = fa[fa[u][k]][k];
		}
	}
	build(1, m, 1);
	For (i, 1, n)
	{
		if (ask(1, m, a[i].l, a[i].r, 1)) continue;
		int l = a[i].l - asksuf0(1, m, 1, a[i].l, 1) + 1,
			r = a[i].r + askpre0(1, m, a[i].r, m, 1) - 1;
		if (getans(l, r) != getans(l, a[i].l - 1)
			+ getans(a[i].r + 1, r) + 1) continue;
		ans[++ansm] = i;
		change(1, m, a[i].l, a[i].r, 1, 1);
	}
	std::cout << ansm << std::endl;
	For (i, 1, ansm) printf("%d ", ans[i]);
	std::cout << std::endl;
	return 0;
}