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的整数,终止日期为不大于109的整数。 输出格式 输出的第一行应有一个整数M,表示最多可以租借给多少家公司。第二行应 1 字典序指在字典中排列的顺序,如果序列l1是序列l2的前缀,或者对于l1和l2的第一个不同位置j,l1[j]<l2[j],则l1比l2小。 2009 亚太地区信息学奥林匹克竞赛 会议中心(CONVENTION) 第 5 页 共 7 页 列出M个数,表示最终将会堂租借给哪些公司。 数据范围 对于50%的输入,N≤3000。在所有输入中,N≤200000。 输入样例 4 4 9 9 11 13 19 10 17 输出样例 2 1 3在不要求字典序的情况下,不难求出一个最优解。
#include <cstdio> #include <cstdlib> #include <algorithm> #include <string> #include <set> using namespace std; const char fi[] = "convention.in"; const char fo[] = "convention.out"; const int maxN = 200010; const int MAX = 0x3f3f3f3f; const int MIN = ~MAX; struct Seg { int L, R; Seg() {} Seg(int L, int R): L(L), R(R) {} bool operator<(const Seg &b) const {return L < b.L || L == b.L && R < b.R;} }; set <Seg> S; set <Seg> ::iterator iter; Seg req[maxN], tmp[maxN], seg[maxN]; int next[20][maxN << 1]; int tab[maxN << 1]; int n, Lim, cnt, logLim; inline void init_file() { freopen(fi, "r", stdin); freopen(fo, "w", stdout); return; } inline int getint() { int res = 0; char tmp; while (!isdigit(tmp = getchar())); do res = (res << 3) + (res << 1) + tmp - '0'; while (isdigit(tmp = getchar())); return res; } inline bool cmp(const Seg &a, const Seg &b) {return a.L < b.L || a.L == b.L && a.R > b.R;} inline int plc(int x) { int L = 0, R = Lim; while (L < R + 1) { int Mid = (L + R) >> 1; if (tab[Mid] == x) return Mid + 1; if (tab[Mid] > x) R = Mid - 1; else L = Mid + 1; } } inline void readdata() { n = getint(); for (int i = 0; i < n; ++i) { int L = getint(), R = getint(); req[i] = Seg(L, R); tab[i << 1] = L; tab[(i << 1) + 1] = R; } return; } inline void discrete() { sort(tab, tab + (n << 1)); tab[Lim++] = tab[0]; for (int i = 1; i < n << 1; ++i) if (tab[i] != tab[i - 1]) tab[Lim++] = tab[i]; //离散化过程。 for (int i = 0; i < n; ++i) tmp[i] = req[i] = Seg(plc(req[i].L), plc(req[i].R)); //重新映射过程。 sort(tmp, tmp + n, cmp); int p = n - 1; seg[cnt++] = tmp[n - 1]; for (int i = n - 2; i > -1; --i) if (tmp[i].R < tmp[p].R) seg[cnt++] = tmp[p = i]; //去掉包含其他区间的区间 //并从按左界从大到小排序。 //(此时又界也是排好序的。) return; } inline void next_set() { int p = 0; next[0][Lim + 1] = MAX; for (int j = Lim; j; --j) if (p < cnt && j == seg[p].L) next[0][j] = seg[p++].R + 1; else next[0][j] = next[0][j + 1]; for (int i = 1; ; ++i) { bool flag = 0; next[i][Lim + 1] = MAX; for (int k = 1; k < Lim + 1; ++k) flag |= (next[i][k] = (next[i - 1][k] == MAX ? MAX : next[i - 1][next[i - 1][k]])) < MAX; if (!flag) {logLim = i - 1; break;} } return; } //建好next表。 inline int max_time(int L, int R) { if (L >= R) return 0; int ans = 0, p = L; ++R; for (int j = logLim; j > -1 && p < R; --j) if (next[j][p] <= R) //注意这里是小于等于符号。 { p = next[j][p]; ans += 1 << j; } //数量从大到小开始放,只要能放下就累加。 return ans; } inline bool judge(int i) { int L = req[i].L, R = req[i].R; iter = S.lower_bound(Seg(L, MAX)); //找到一个左界刚刚大于L的区间并取其前驱。 if (iter-- == S.begin()) return 0; //若无前驱则返回0。 if (iter -> R < R || iter -> L > L) return 0; //若找到的这个区间不能包含被枚举的区间[L, R]则返回0。 int L1 = iter -> L, R1 = iter -> R; if (max_time(L1, L - 1) + max_time(R + 1, R1) + 1 < max_time(L1, R1)) return 0; S.erase(iter); if (L1 < L) S.insert(Seg(L1, L - 1)); if (R < R1) S.insert(Seg(R + 1, R1)); return 1; } inline void work() { next_set(); printf("%d\n", max_time(1, Lim)); S.insert(Seg(1, Lim)); //首先把初始区域插入到平衡树中。 for (int i = 0; i < n; ++i) if (judge(i)) printf("%d ", i + 1); printf("\n"); return; } int main() { init_file(); readdata(); discrete(); work(); return 0; }
第二次做:
#include <cstdio> #include <cstdlib> #include <algorithm> #include <string> #include <set> const char fi[] = "convention.in"; const char fo[] = "convention.out"; const int maxN = 200010; const int MAX = 0x3f3f3f3f, MIN = ~MAX; struct Seg { int L, R; Seg() {} Seg(int L, int R): L(L), R(R) {} bool operator<(const Seg &b) const {return L < b.L || L == b.L && R < b.R;} }; std::set <Seg> S; std::set <Seg>::iterator iter; Seg req[maxN], seg[maxN], tmp[maxN]; int tab[maxN << 1], next[20][maxN << 1]; int n, cnt, Lim = 1, logLim; void init_file() { freopen(fi, "r", stdin); freopen(fo, "w", stdout); return; } inline int getint() { int res = 0; char tmp; while (!isdigit(tmp = getchar())); do res = (res << 3) + (res << 1) + tmp - '0'; while (isdigit(tmp = getchar())); return res; } void readdata() { n = getint(); for (int i = 0; i < n; ++i) { int L = getint(), R = getint(); req[i] = Seg(L, R); tab[i << 1] = L; tab[(i << 1) + 1] = R; } return; } int plc(int x) { for (int L = 0, R = Lim - 1; L < R + 1;) { int Mid = (L + R) >> 1; if (x == tab[Mid]) return Mid + 1; if (x < tab[Mid]) R = Mid - 1; else L = Mid + 1; } } bool cmp(const Seg &a, const Seg &b) {return a.R < b.R || a.R == b.R && a.L > b.L;} void discrete() { std::sort(tab, tab + (n << 1)); for (int i = 1; i < n << 1; ++i) if (tab[i] != tab[i - 1]) tab[Lim++] = tab[i]; for (int i = 0; i < n; ++i) tmp[i] = req[i] = Seg(plc(req[i].L), plc(req[i].R)); std::sort(tmp, tmp + n, cmp); //这里必须要用一个临时数组, //保证左界右界同时单调递增。 int p = 0; seg[cnt++] = tmp[0]; for (int i = 1; i < n; ++i) if (tmp[i].L > tmp[p].L) seg[cnt++] = tmp[p = i]; return; } void next_set() { int p = cnt; next[0][Lim + 1] = MAX; for (int j = Lim; j; --j) if (p > -1 && j == seg[p - 1].L) next[0][j] = seg[--p].R + 1; else next[0][j] = next[0][j + 1]; for (int i = 0; ; ++i) { bool flag = 0; next[i + 1][Lim + 1] = MAX; for (int k = 1; k < Lim + 1; ++k) { if (next[i][k] == MAX) next[i + 1][k] = MAX; else next[i + 1][k] = next[i][next[i][k]]; if (next[i + 1][k] < MAX) flag = 1; } if (!flag) {logLim = i; break;} } return; } int max_time(int L, int R) { if (L > R++) return 0; int ans = 0, p = L; for (int i = logLim; i > -1 && p < R; --i) if (next[i][p] <= R) {p = next[i][p]; ans += 1 << i;} return ans; } bool query(int i) { int L = req[i].L, R = req[i].R; iter = S.lower_bound(Seg(L, MAX)); if (iter-- == S.begin()) return 0; if (iter -> L > L || iter -> R < R) return 0; int L1 = iter -> L, R1 = iter -> R; if (max_time(L1, L - 1) + max_time(R + 1, R1) + 1 < max_time(L1, R1)) //这里要满足放进去过后不影响总的答案。 return 0; S.erase(iter); if (L1 < L) S.insert(Seg(L1, L - 1)); if (R < R1) S.insert(Seg(R + 1, R1)); return 1; } void work() { printf("%d\n", max_time(1, Lim)); S.insert(Seg(1, Lim)); for (int i = 0; i < n; ++i) if (query(i)) printf("%d ", i + 1); printf("\n"); return; } int main() { init_file(); readdata(); discrete(); next_set(); work(); return 0; }