[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 */