bzoj 1178: [Apio2009]CONVENTION会议中心
来源:互联网 发布:淘宝评价如何修改 编辑:程序博客网 时间:2024/06/11 21:55
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1178
题解:这是一道思路不错的题目。
考虑到我们可以贪心直接出解,然而无法做到字典序最小,考虑,按字典序从小到大枚举线段,如果满足可以放置就放下,这样贪心一定可以得到答案,矛盾点就在于如何判断一条线段是否应放下。
先从简单入手,考虑第一条需要放置的线段,明显如果枚举到线段x,而线段x处于某个最优解中那x一定是第一条放置的线段,考虑如何判断线段x是否处于当前局面的某个最优解,按照一般的套路,我们考虑dp,用f,g数组分别处理出从左向右,从右向左的最大不相交线段数(即题目要求的),然后判断f + g + 1 是否等于贪心出的最优解,若等于则存在,于是我们发现这相当于分治,将整个区间一分为二,左边的限制是f,右边的限制是g,成为了递归子问题,如果每次都dp一遍会很慢,最好也只能做到O(n^2),考虑改变dp形式,然后优化它,想到这一切都很简单了,设f[i]表示从当前点向后得到i个不相交线段其端点最靠左是多少,我们发现f数组可合并,所以倍增优化它,类似ST表,剩下的各种操作都可以用线段树支持了,我用了标记永久化然而慢到要死。
代码:
#include<iostream>#include<cstdio>#include<string>#include<cstring>#include<algorithm>#define inf 1e9#define N 200000using namespace std;struct node{ int l,r;};struct data{ int l,r,val;};node seg[N + 5];data A[(N << 3) + 5],findl,findr;int n,a[(N << 1) + 5],num,f[(N << 1) + 5][22],maxi,ans;bool lef[(N << 1) + 5];inline int getnum(){ char c; int num,flag = 1; while (!isdigit(c = getchar())) if (c == '-') flag = -1; num = c - '0'; while (isdigit(c = getchar())) num = 10 * num + c - '0'; return num * flag;}inline void init(){ n = getnum(); for (int i = 1;i <= n; ++i) seg[i].l = getnum(),seg[i].r = getnum(); for (int i = 1;i <= n; ++i) a[(i<<1) - 1] = seg[i].l,a[(i<<1)] = seg[i].r; sort(a + 1,a + (n << 1) + 1); num = unique(a + 1,a + (n << 1) + 1) - a - 1; for (int i = 1;i <= n; ++i){ seg[i].l = lower_bound(a + 1,a + num + 1,seg[i].l) - a; seg[i].r = lower_bound(a + 1,a + num + 1,seg[i].r) - a; } memset(lef,0,sizeof(lef)); for (int i = 1;i <= n; ++i) lef[seg[i].l] = 1;}inline void build(int l,int r,int rt){ int mid = (r + l)>>1; A[rt].l = 1; A[rt].r = num; A[rt].val = ans; if (l == r) return; build(l,mid,rt<<1); build(mid + 1,r,rt<<1|1);}inline data query(int l,int r,int rt,int pos){ int mid = (r + l)>>1; if (l == r) return A[rt]; data t; if (pos <= mid) t = query(l,mid,rt<<1,pos); else t = query(mid + 1,r,rt<<1|1,pos); t.l = max(t.l,A[rt].l); t.r = min(t.r,A[rt].r); t.val = min(t.val,A[rt].val); return t;}inline int calc(int ll,int rr){ int sum = 0,j = ll; if (ll < 1||rr > num||ll > rr) return 0; for (int i = maxi;i >= 0;--i) if (f[j][i] <= rr){ sum += (1 << i); j = f[j][i] + 1; if (j > num) return sum; } return sum;}inline void change(int l,int r,int rt,int ll,int rr,int a,int b,int c){ if (ll < 1||rr > num||ll > rr) return; int mid = (r + l)>>1; if (ll <= l&&rr >= r){ A[rt].l = a; A[rt].r = b; A[rt].val = c; return; } if (ll <= mid) change(l,mid,rt<<1,ll,rr,a,b,c); if (rr > mid) change(mid + 1,r,rt<<1|1,ll,rr,a,b,c);}inline bool can_put(int x){ findl = query(1,num,1,seg[x].l); findr = query(1,num,1,seg[x].r); if (findl.val != -1&&findr.val != -1&&findl.l == findr.l&&findl.r == findr.r) return 1; return 0;}inline void DO_IT(){ memset(f,127,sizeof(f)); for (int i = 1;i <= n; ++i) f[seg[i].l][0] = min(f[seg[i].l][0],seg[i].r); int mini = inf; for (int i = num;i >= 1; --i){ if (lef[i]) mini = min(mini,f[i][0]); f[i][0] = min(f[i][0],mini); } maxi = 20; for (int i = 1;i <= maxi; ++i) for (int j = 1;j <= num; ++j) if (f[j][i - 1] >= inf||f[j][i - 1] == num) f[j][i] = inf; else f[j][i] = f[f[j][i - 1] + 1][i - 1]; int j = 1; ans = calc(1,num); printf("%d\n",ans); build(1,num,1); int lnum,rnum,numm = 0; for (int i = 1;i <= n; ++i) if (can_put(i)){ lnum = calc(findl.l,seg[i].l - 1); rnum = calc(seg[i].r + 1,findr.r); if (lnum + rnum + 1 == findl.val) { ++numm; if (numm == ans) printf("%d",i); else printf("%d ",i); change(1,num,1,seg[i].l,seg[i].r,-1,-1,-1); change(1,num,1,findl.l,seg[i].l - 1,findl.l,seg[i].l - 1,lnum); change(1,num,1,seg[i].r + 1,findr.r,seg[i].r + 1,findr.r,rnum); } }}int main(){ //freopen("ou.in","r",stdin); //freopen("ou.out","w",stdout); init(); DO_IT(); //fclose(stdin); fclose(stdout); return 0;}
1.写线段树的时候注意最底层是A[rt]不是A[l] !!!
2.根据具体值判断数组开的大小
0 0
- 【BZOJ 1178】 [Apio2009]CONVENTION会议中心
- bzoj 1178: [Apio2009]CONVENTION会议中心
- BZOJ 1178 [Apio2009]CONVENTION会议中心【】
- [BZOJ 1178][Apio2009]CONVENTION会议中心:贪心+倍增
- bzoj 1178: [Apio2009]CONVENTION会议中心 倍增+set
- 1178: [Apio2009]CONVENTION会议中心
- bzoj 1178: [Apio2009]CONVENTION会议中心 (set+倍增)
- bzoj1178: [Apio2009]CONVENTION会议中心
- BZOJ1178: [Apio2009]CONVENTION会议中心
- bzoj1178: [Apio2009]CONVENTION会议中心
- [Apio2009]CONVENTION会议中心 解题报告
- [BZOJ1178][Apio2009]CONVENTION会议中心(单调栈+dp+set)
- apio2009 convention
- BZOJ 1178 APIO 2009 会议中心
- 【BZOJ 1177】 [Apio2009]Oil
- BZOJ 1179: [Apio2009]Atm
- BZOJ-1177 [Apio2009]Oil
- BZOJ P1179[Apio2009]Atm
- Android中AutoCompleteTextView控件的使用
- 6个变态的C语言写的Hello World【转】
- HDU 5867 Water problem(模拟题目)【HDU多校联合8.18】
- hdu 2577 How To Type
- 第三方XlistView
- bzoj 1178: [Apio2009]CONVENTION会议中心
- 使用谷歌开发工具的好处
- Codeforces Round #360 (Div. 2) B Lovely Palindromes
- Android 应用第一次运行时,引导页面的设置方法(只让程序Acitivity运行一次的设置方法)
- 横向导航的布局
- Ajax的同步和异步数据传输
- leetcode 155 Min Stack C++
- 数据库抽象层——PDO
- C++继承