1178: [Apio2009]CONVENTION會議中心 Time Limit: 15 Sec Memory Limit: 162 MB
Submit: 824 Solved: 327
[ Submit][ Status][ Discuss] Description
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)。 你的任務是協助銷售主管確定應該將會堂租借給哪些公司。 Input
輸入的第一行有一個整數N,表示發出租借會堂申請的公司的個數。第2到第N+1行每行有2個整數。第i+1行的整數表示第i家公司申請租借的起始和終止日期。對於每個公司的申請,起始日期為不小於1的整數,終止日期為不大於10^9的整數。N≤200000 Output
輸出的第一行應有一個整數M,表示最多可以租借給多少家公司。第二行應列出M個數,表示最終將會堂租借給哪些公司。 Sample Input 4
4 9
9 11
13 19
10 17 Sample Output 2
1 3 HINT
修複資料bug,並新加資料一組By NanoApe 2016.5.11
修複後資料:JudgeOnline/upload/201605/dd.rar
Source
[ Submit][ Status][ Discuss]
去掉第二個詢問就是經典貪心,,,
現在問題是第二個要怎麼處理。。。
迴歸第一問
我們去掉所有內含項目關聯性的區間中較大的那個,對結果一定不會有影響
因為如果某個最優方案有那個大的區間,那選小的一定沒有差別
這樣我們就得到一些小區間,這些區間的左右端點都是嚴格遞增的
當我們從這些區間裡選擇最多的,兩兩不相交的區間,那就是第一問的答案了
貪心即可
考慮第二問,
對於一個區間,如果選擇它,對最優答案沒有影響,那顯然這個區間是可以選的
這樣按時間順序考慮每個區間,每次檢查這個區間選了會不會讓答案變差,如果不會,就要它
如何檢查。。
顯然,當前考慮的區間中,若有一個位置已經被覆蓋過,那這個區間一定不能選
若全部為空白,那這個區間一定包含於一個較大的空區間,記這個空區間為[i,j],記當前區間[l,r]
那麼如果我們知道[i,j]、[i,l)以及(r,j]的最優答案,就能判斷了
而之前已經處理出一些兩兩不互相包含的區間
將它們按照左端點升序排序
記Right[i]為第i個區間向右,第一個與它不相交的區間
對於[l,r],我們找出所有包含於[l,r]的區間,不斷地沿Right數組走,就能找出答案
這走法可以用倍增演算法最佳化成O(logn)
#include<iostream>#include<cstdio>#include<queue>#include<vector>#include<bitset>#include<algorithm>#include<cstring>#include<map>#include<stack>#include<set>#include<cmath>#include<ext/pb_ds/priority_queue.hpp>using namespace std; const int maxn = 4E5 + 10;const int INF = ~0U>>1;const int T = 16; int n,cnt,top,tot,L[maxn],R[maxn],L2[maxn],R2[maxn],c[maxn*T] ,cur = 1,ans,Right[maxn][20],s[maxn],Num[maxn*2];bool Mark[maxn*T]; void Read(){ cin >> n; for (int i = 1; i <= n; i++) { scanf("%d%d",&L[i],&R[i]); Num[++tot] = L[i]; Num[++tot] = R[i]; } sort(Num + 1,Num + tot + 1); for (int i = 2; i <= tot; i++) if (Num[i] != Num[i-1]) Num[++cur] = Num[i]; Num[++cur] = INF; L[0] = R[0] = cur; for (int i = 1; i <= n; i++) { L[i] = lower_bound(Num + 1,Num + cur + 1,L[i]) - Num; R[i] = lower_bound(Num + 1,Num + cur + 1,R[i]) - Num; if (R[R2[L[i]]] > R[i]) R2[L[i]] = i; }} void Work(){ for (int i = 1; i <= cur; i++) { if (!R2[i]) continue; while (top && R[s[top]] >= R[R2[i]]) --top; s[++top] = R2[i]; } for (int i = 1; i <= top; i++) L2[i] = L[s[i]],R2[i] = R[s[i]]; int Now = top + 1; L2[Now] = R2[Now] = cur; for (int i = top; i; i--) { while (L2[Now - 1] > R2[i]) --Now; Right[i][0] = Now; if (Right[i][0] > top) Right[i][0] = 0; } for (int j = 1; j < 20; j++) for (int i = 1; i <= top; i++) Right[i][j] = Right[Right[i][j-1]][j-1];} int Query(int l,int r){ if (l > r) return 0; int pl = lower_bound(L2 + 1,L2 + top + 1,l) - L2; int pr = lower_bound(R2 + 1,R2 + top + 1,r) - R2; if (R2[pr] > r || pr > top) --pr; if (pl > pr) return 0; int pos = pl,ret = 1; for (int j = 19; j >= 0; j--) { if (!Right[pos][j]) continue; if (Right[pos][j] <= pr) { ret += (1<<j); pos = Right[pos][j]; } } return ret;} void pushdown(int o,int l,int r){ if (Mark[o]) { Mark[o<<1] = Mark[o<<1|1] = 1; int mid = (l + r) >> 1; c[o<<1] = mid - l + 1; c[o<<1|1] = r - mid; }} void Modify(int o,int l,int r,int ml,int mr){ if (ml <= l && r <= mr) { Mark[o] = 1; c[o] = r - l + 1; return; } pushdown(o,l,r); int mid = (l + r) >> 1; if (ml <= mid) Modify(o<<1,l,mid,ml,mr); if (mr > mid) Modify(o<<1|1,mid+1,r,ml,mr); if (Mark[o<<1] && Mark[o<<1|1]) Mark[o] = 1; c[o] = c[o<<1] + c[o<<1|1];} int Lower(int o,int l,int r,int ql,int qr){ int mid = (l + r) >> 1; if (ql <= l && r <= qr) { if (!c[o]) return cur; if (Mark[o]) return l; pushdown(o,l,r); if (c[o<<1]) return Lower(o<<1,l,mid,ql,qr); else return Lower(o<<1|1,mid + 1,r,ql,qr); } pushdown(o,l,r); int ret = cur; if (ql <= mid) ret = min(ret,Lower(o<<1,l,mid,ql,qr)); if (ret < cur) return ret; if (qr > mid) ret = min(ret,Lower(o<<1|1,mid+1,r,ql,qr)); return ret;} int Upper(int o,int l,int r,int ql,int qr){ int mid = (l + r) >> 1; if (ql <= l && r <= qr) { if (!c[o]) return 0; if (Mark[o]) return r; pushdown(o,l,r); if (c[o<<1|1]) return Upper(o<<1|1,mid+1,r,ql,qr); else return Upper(o<<1,l,mid,ql,qr); } pushdown(o,l,r); int ret = 0; if (qr > mid) ret = max(ret,Upper(o<<1|1,mid+1,r,ql,qr)); if (ret) return ret; if (ql <= mid) ret = max(ret,Upper(o<<1,l,mid,ql,qr)); return ret;} int Sum(int o,int l,int r,int ql,int qr){ if (ql <= l && r <= qr) return c[o]; pushdown(o,l,r); int mid = (l + r) >> 1,ret = 0; if (ql <= mid) ret += Sum(o<<1,l,mid,ql,qr); if (qr > mid) ret += Sum(o<<1|1,mid+1,r,ql,qr); return ret;} int main(){ //freopen("1178.in","r",stdin); //freopen("1178.out","w",stdout); Read(); Work(); Modify(1,0,cur,0,0); Modify(1,0,cur,cur,cur); ans = Query(1,cur); printf("%d\n",ans); int K = 0; for (int i = 1; i <= n; i++) { if (Sum(1,0,cur,L[i],R[i]) > 0) continue; int l = Upper(1,0,cur,0,L[i] - 1); int r = Lower(1,0,cur,R[i] + 1,cur); ++l; --r; int Need = Query(l,r); int suml = Query(l,L[i] - 1); int sumr = Query(R[i] + 1,r); if (suml + sumr + 1 == Need) { ++K; if (K < ans) printf("%d ",i); else printf("%d",i); Modify(1,0,cur,L[i],R[i]); } } return 0;}