[BZOJ1178][Apio2009]CONVENTION会议中心

[BZOJ1178][Apio2009]CONVENTION会议中心

试题描述

Siruseri政府建造了一座新的会议中心。许多公司对租借会议中心的会堂很感兴趣,他们希望能够在里面举行会议。 对于一个客户而言,仅当在开会时能够独自占用整个会堂,他才会租借会堂。会议中心的销售主管认为:最好的策略应该是将会堂租借给尽可能多的客户。显然,有可能存在不止一种满足要求的策略。 例如下面的例子。总共有4个公司。他们对租借会堂发出了请求,并提出了他们所需占用会堂的起止日期(如下表所示)。 开始日期 结束日期 公司1 4 9 公司2 9 11 公司3 13 19 公司4 10 17 上例中,最多将会堂租借给两家公司。租借策略分别是租给公司1和公司3,或是公司2和公司3,也可以是公司1和公司4。注意会议中心一天最多租借给一个公司,所以公司1和公司2不能同时租借会议中心,因为他们在第九天重合了。 销售主管为了公平起见,决定按照如下的程序来确定选择何种租借策略:首先,将租借给客户数量最多的策略作为候选,将所有的公司按照他们发出请求的顺序编号。对于候选策略,将策略中的每家公司的编号按升序排列。最后,选出其中字典序最小1的候选策略作为最终的策略。 例中,会堂最终将被租借给公司1和公司3:3个候选策略是{(1,3),(2,3),(1,4)}。而在字典序中(1,3)<(1,4)<(2,3)。 你的任务是帮助销售主管确定应该将会堂租借给哪些公司。

输入

输入的第一行有一个整数N,表示发出租借会堂申请的公司的个数。第2到第N+1行每行有2个整数。第i+1行的整数表示第i家公司申请租借的起始和终止日期。对于每个公司的申请,起始日期为不小于1的整数,终止日期为不大于10^9的整数。

输出

输出的第一行应有一个整数M,表示最多可以租借给多少家公司。第二行应列出M个数,表示最终将会堂租借给哪些公司。

输入示例

4
4 9
9 11
13 19
10 17

输出示例

2
1 3 

数据规模及约定

对于50%的输入,N≤3000。在所有输入中,N≤200000 请注意使用Readln读入数据........

题解

这里把所有的“公司”等价成左端点为“开始日期”,右端点为“结束日期”的线段。

如果只考虑第一问,贪心即可,按右端点升序排序,把所有包含别的线段的线段去掉数个数即可。

加了个字典序,就好玩了。

然而“字典序”还是要用到贪心,于是从1~n条线段依次判断,若第 i 条线段满足加进去后不影响最终能选的线段条数则毫无疑问地选择 i。如何判断是核心问题,我们可以用倍增算法:令 f(j, i) 表示从点 i 开始,选择 2j 个不相交线段可能的最靠左的点,那么就不难算出一个区间 [L, R] 中最优能选择的线段条数了(详见代码中的 getans() 函数)。以后判断线段时,先确保当前线段与前面所有加进来的线段无交集,再找到这个线段的前驱 ql 和后继 qr,然后当 ans(ql + 1, qr - 1) = ans(ql + 1, l - 1) + 1 + ans(r + 1, qr - 1) 时说明这个线段可以选进来(ans(i, j)表示区间[i, j]中最优能够选择的线段的条数,l、r 表示当前线段左、右端点)。

至于判断线段是否有交集,可以用个平衡树找前驱后继。(如果不怕慢可以用set)

代码注释多,请忽略。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *tail;
inline char Getchar() {
    if(Head == tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 200010
#define maxm 400010
#define maxlog 20
#define oo 2147483647
int n, N, cnt, num[maxm], c, ans[maxn];
struct Line { int l, r, id; } ls[maxn];

int Right[maxlog][maxm];
void init() {
	for(int j = 1; j < maxlog; j++)
		for(int i = 1; i <= N && Right[j-1][i] < N && Right[j-1][Right[j-1][i]+1] <= N; i++)
			Right[j][i] = Right[j-1][Right[j-1][i]+1];
	return ;
}
int getans(int l, int r) {
	if(l > r) return 0;
	int ans = 0, p = l;
	for(int i = maxlog-1; i >= 0; i--) if(Right[i][p] <= r)
		ans += (1 << i), p = Right[i][p];
	return ans;
}

int ToT, fa[maxm], ch[maxm][2], root;
struct Node { int v, r, minv, maxv, siz; } nodes[maxm];
void newnode(int& o, int v) {
	nodes[o = ++ToT] = (Node) { v, rand(), v, v, 1 };
	return ;
}
void maintain(int o) {
	Node &u = nodes[o], &lc = nodes[ch[o][0]], &rc = nodes[ch[o][1]];
	u.minv = min(min(lc.minv, rc.minv), u.v);
	u.maxv = max(max(lc.maxv, rc.maxv), u.v);
	u.siz = lc.siz + 1 + rc.siz;
	return ;
}
void rotate(int u) {
	int y = fa[u], z = fa[y], l = 0, r = 1;
	if(ch[y][1] == u) swap(l, r);
	if(z >= 0) ch[z][ch[z][1]==y] = u;
	else root = u;
	fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
	ch[y][l] = ch[u][r]; ch[u][r] = y;
	maintain(y); maintain(u);
	return ;
}
void insert(int& o, int v, int pa) {
	if(!o) {
		newnode(o, v);
		fa[o] = pa;
		return ;
	}
	int d = v > nodes[o].v;
	insert(ch[o][d], v, o);
	if(nodes[ch[o][d]].r > nodes[o].r) rotate(ch[o][d]);
	maintain(o);
	return ;
}
int _l, _r, _s;
void findp(int o, int v) {
//	printf("\no: %d %d %d %d\n", o, nodes[o].v, ch[o][0], nodes[ch[o][0]].maxv);
	if(!o) { _l = _r = -1; return ; }
	if(nodes[o].v == v){ _l = _r = v; return ; }
	if(nodes[o].v > v && ch[o][0] && nodes[ch[o][0]].maxv < v) {
		_s += nodes[ch[o][0]].siz;
		_l = nodes[ch[o][0]].maxv; _r = nodes[o].v;
		return ;
	}
	if(nodes[o].v < v && ch[o][1] && nodes[ch[o][1]].minv > v) {
		_s += nodes[ch[o][0]].siz + 1;
		_l = nodes[o].v; _r = nodes[ch[o][1]].minv;
		return ;
	}
	int d = v > nodes[o].v;
	if(d) _s += nodes[ch[o][0]].siz + 1;
	findp(ch[o][d], v);
	return ;
}

int main() {
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	srand(1314);
	n = read(); N = n << 1;
	for(int i = 1; i <= n; i++) {
		num[++cnt] = ls[i].l = read(); num[++cnt] = ls[i].r = read();
		ls[i].id = i;
	}
	sort(num + 1, num + cnt + 1);
	unique(num + 1, num + cnt + 1);
	for(int i = 1; i <= n; i++) {
		ls[i].l = lower_bound(num + 1, num + cnt + 1, ls[i].l) - num;
		ls[i].r = lower_bound(num + 1, num + cnt + 1, ls[i].r) - num;
	}
	
	int p = 0;
	for(int j = 0; j < maxlog; j++)
		for(int i = 1; i <= N; i++) Right[j][i] = N + 1;
	for(int i = 1; i <= n; i++) Right[0][ls[i].l] = min(Right[0][ls[i].l], ls[i].r);
//	for(int i = 1; i <= n; i++) printf("%d %d\n", ls[i].l, ls[i].r);
	for(int i = N - 1; i; i--) Right[0][i] = min(Right[0][i], Right[0][i+1]);
//	for(int i = 1; i <= N; i++) printf("%d ", Right[0][i]); putchar('\n');
	init();
	printf("%d\n", getans(1, N));
	nodes[0] = (Node) { 0, 0, oo, -oo, 0 };
	insert(root, 0, -1); insert(root, N + 1, -1);
	for(int i = 1; i <= n; i++) {
		findp(root, ls[i].l); int ll = _l, lr = _r; _s = 0;
		findp(root, ls[i].r); int rl = _l, rr = _r;
//		printf("%d [%d, %d]: %d %d %d %d %d\n", i, ls[i].l, ls[i].r, ll, lr, rl, rr, _s);
		if(ll == lr || rl == rr || ll != rl || lr != rr || !(_s & 1)) continue;
		ll++; lr--;
		if(getans(ll, lr) == getans(ll, ls[i].l-1) + 1 + getans(ls[i].r+1, lr)) {
			ans[++c] = i; insert(root, ls[i].l, -1); insert(root, ls[i].r, -1);
		}
	}
//	printf("getans: %d %d\n", getans(1, 3), Right[0][1]);
	
	for(int i = 1; i <= c; i++) printf("%d ", ans[i]); putchar('\n');
//	printf("%d\n", c);
	
	return 0;
}
/*
5
4 8
3 6
1 4
1 3
9 10
*/

 

posted @ 2016-04-24 21:53  xjr01  阅读(997)  评论(0编辑  收藏  举报