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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 1个月新生儿便秘怎么办 新生儿头竖立0分怎么办 20天的宝宝便秘怎么办 出生23天的宝宝便秘怎么办 喝奶粉的宝宝便秘怎么办 抱孩子抱的驼背怎么办 4岁宝宝不拉屎怎么办 小朋友大便拉不出来怎么办 儿童便秘拉不出来怎么办 没感冒喉咙有痰怎么办 宝宝喂不进去药怎么办 新生儿只放屁不拉大便怎么办 新生儿腹胀不拉大便怎么办 8月宝宝咳嗽有痰怎么办 2个月婴儿惊吓怎么办 吃了米粉不拉屎怎么办 奇异果奶昔苦了怎么办 8个月宝宝偏瘦怎么办 一岁宝宝螺旋腿怎么办 七个月宝宝晚上咳嗽厉害怎么办 孕7个月感冒咳嗽怎么办 4个月婴儿肺炎怎么办 宝宝吃胡萝卜泥拉肚子怎么办 宝宝吃土豆泥不消化怎么办 五个月宝宝奶睡怎么办 橘子和牛奶一起吃了怎么办 半岁宝宝不喝水怎么办 一岁宝宝总是便秘怎么办 1岁的宝宝不喝水怎么办 两月宝宝不喝水怎么办 破壁机打出的果汁很浓稠怎么办 榨的果汁不甜怎么办 11个月宝宝吃盐怎么办 1岁半宝宝长牙慢怎么办 小孩里面的牙黑怎么办 7个半月宝宝便秘怎么办 2个月婴幼儿便秘怎么办 3个月宝宝不消化怎么办 一天大便二三次不消化怎么办 不消化没胃口几天不大便怎么办? 两个月的宝宝拉绿屎怎么办