BZOJ1178或洛谷3626 [APIO2009]会议中心

BZOJ原题链接

洛谷原题链接

第一个问题是经典的最多不相交区间问题,用贪心即可解决。
主要问题是第二个,求最小字典序的方案。
我们可以尝试从(1 o n)扫一遍所有区间,按顺序对每一个不会使答案变差的区间都尝试着去填,这样就可以保证方案的字典序最小。
考虑如果快速判断该区间是否能成为最优解,开头先按右端点从小到大排序,左端点从大到小排序,再去除有包含关系的区间,这样使得讨论更为简单。
设待插入的区间为([r,l]),该区间左边的第一个已插入的区间的右端点为(L),右边的第一个已插入的区间的左端点为(R)(S[i][j])表示([i,j])间有多少已插入的区间。
则该区间能插入必须满足(S[L + 1][R - 1] = S[L + 1][r - 1] + S[l + 1][R - 1] + 1)
对于快速计算(S),我们可以采用倍增的思想,设(ne[x][i])表示第(x)个区间后选择(2^i)个区间的最后一个区间下标,可以使用倍增在(nlogn)内预处理出来。
维护已插入区间可以使用(C++ STL set)

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
const int N = 2e5 + 10;
const int M = 18;
struct dd{
	int x, y;
	bool operator < (const dd &b) const
	{
		if (!(y ^ b.y))
			return x > b.x;
		return y < b.y;
	}
};
dd a[N], b[N];
int X[N], Y[N], ne[N][M], m, gn;
set<dd>S;
inline int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c < '0' || c > '9'; c = getchar())
		p |= c == '-';
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + c - '0';
	return p ? -x : x;
}
inline int lowbod(int x)
{
	int l = 1, r = m, mid, an = m + 1;
	while (l <= r)
	{
		mid = (l + r) >> 1;
		if (X[mid] >= x)
		{
			an = mid;
			r = mid - 1;
		}
		else
			l = mid + 1;
	}
	return an;
}
inline int calc(int l, int r)
{
	int x = lowbod(l);
	if (x > m || Y[x] > r)
		return 0;
	int s = 1;
	for (int i = gn; ~i; i--)
		if (ne[x][i] && Y[ne[x][i]] <= r)
		{
			s += 1 << i;
			x = ne[x][i];
		}
	return s;
}
int main()
{
	int i, j, n, l, r, L, R;
	n = re();
	for (i = 1; i <= n; i++)
	{
		a[i].x = re();
		a[i].y = re();
		b[i] = a[i];
	}
	sort(b + 1, b + n + 1);
	for (i = 1; i <= n; i++)
		if (b[i].x > b[m].x || !m)
			b[++m] = b[i];
	for (i = 1; i <= m; i++)
	{
		X[i] = b[i].x;
		Y[i] = b[i].y;
	}
	for (i = j = 1; i <= m; i++)
	{
		for (; j <= m && b[j].x <= b[i].y; j++);
		if (j <= m)
			ne[i][0] = j;
	}
	gn = log2(m);
	for (j = 1; j <= gn; j++)
		for (i = 1; i <= m; i++)
			ne[i][j] = ne[ne[i][j - 1]][j - 1];
	printf("%d
", calc(-2e9, 2e9));
	S.insert((dd){-2e9, -2e9});
	S.insert((dd){2e9, 2e9});
	for (i = 1; i <= n; i++)
	{
		set<dd>::iterator x = S.lower_bound(a[i]), y;
		--(y = x);
		L = y -> y;
		r = a[i].x;
		l = a[i].y;
		R = x -> x;
		if (L >= r || l >= R)
			continue;
		if (!(calc(L + 1, R - 1) ^ (calc(L + 1, r - 1) + calc(l + 1, R - 1) + 1)))
		{
			printf("%d ", i);
			S.insert(a[i]);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Iowa-Battleship/p/9794672.html