[BZOJ1178][Apio2009]CONVENTION会议中心(倍增 + 贪心 + 线段树)

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,l1)+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值