BZOJ1178 [Apio2009]CONVENTION会议中心 贪心 set
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1178
题意概括
一堆线段,现在取出最多条数,使其互不覆盖,并输出字典序最小的方案。
题解
这题好坑。
首先,注意一点,最后不能有多余的空格。
第一问就是基础的线段覆盖。
关键在于第二问。
我们要准备一个函数—— Get_Ans(L,R),用来求解L~R这个区间内,最多可以取多少线段。
这个可以O(log n)解决。前提是倍增预处理。
然后就是关键部分。
我们按照字典序从小到大,能选择就选择。
怎么判断是否可以选择?只需要在之前选择的线段空隙中可以放这个线段,并且满足Get_Ans(这个线段到它左边的第一条线段之前)+Get_Ans(它到它右边的第一个线段之前)+1 = Get_Ans(他左右两端的),那么就可以放进去。
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cmath> #include <cstdlib> #include <set> using namespace std; const int N=200000+5,Inf=1e9; struct seg{ int L,R; bool operator < (const seg x) const { if (L==x.L) return R>x.R; return L<x.L; } }s[N],s2[N]; int n,rn,Hash[N*2],hs,p[N][20],L[N]; bool alive[N],vis[N*2]; set <int> S; void HASH(){ sort(Hash+1,Hash+hs+1); int hs_=1; for (int i=2;i<=hs;i++) if (Hash[i]!=Hash[i-1]) Hash[++hs_]=Hash[i]; hs=hs_; } void Delete(){ for (int i=1;i<=n;i++) s2[i]=s[i]; sort(s+1,s+n+1); memset(alive,true,sizeof alive); int MinR=Inf,n_=0; for (int i=n;i>=1;i--) if (MinR>s[i].R) MinR=s[i].R; else alive[i]=0; for (int i=1;i<=n;i++) if (alive[i]) s[++n_]=s[i]; n=n_; } void Get_Pos(){ memset(p,0,sizeof p); for (int i=1;i<=n;i++) L[i]=s[i].L; L[n+1]=Inf; for (int i=1;i<=n;i++){ p[i][0]=upper_bound(L+1,L+n+2,s[i].R)-L; if (p[i][0]==n+1) p[i][0]=0; } for (int i=1;i<=18;i++) for (int j=1;j<=n;j++) p[j][i]=p[p[j][i-1]][i-1]; } int Get_Ans(int Le,int Ri){ if (Le>Ri||Le>hs) return 0; int ans=1,pos=lower_bound(L+1,L+n+2,Le)-L; if (pos>n||Ri<s[pos].R) return 0; for (int i=18;i>=0;i--){ if (!p[pos][i]||s[p[pos][i]].R>Ri) continue; pos=p[pos][i]; ans+=1<<i; } return ans; } int main(){ scanf("%d",&n),rn=n; for (int i=1;i<=n;i++){ scanf("%d%d",&s[i].L,&s[i].R); Hash[++hs]=s[i].L; Hash[++hs]=s[i].R; } HASH(); for (int i=1;i<=n;i++){ s[i].L=lower_bound(Hash+1,Hash+hs+1,s[i].L)-Hash; s[i].R=lower_bound(Hash+1,Hash+hs+1,s[i].R)-Hash; } Delete(); Get_Pos(); printf("%d\n",Get_Ans(1,hs)); S.clear(); S.insert(Inf),S.insert(-Inf); bool empty_block=0; for (int i=1;i<=rn;i++){ int now=*S.lower_bound(s2[i].L); if (now==Inf||!vis[now]){ if (now<=s2[i].R) continue; int left=*--S.lower_bound(s2[i].L); int right=*S.lower_bound(s2[i].R); int val=Get_Ans(left+1,s2[i].L-1)+1+Get_Ans(s2[i].R+1,right-1); if (val<Get_Ans(left+1,right-1)) continue; S.insert(s2[i].L); S.insert(s2[i].R); if (s2[i].L!=s2[i].R) vis[s2[i].R]=1; if (empty_block) putchar(' '); printf("%d",i); empty_block=1; } } return 0; }