zoukankan      html  css  js  c++  java
  • 【BZOJ3050】【USACO 2013 Jan Gold金组】坐座位 Seating

    【USACO 2013 1月金组】seating

    时间限制: 1 Sec 内存限制: 128 MB

    题目描述

    为了赚更多的钱,奶牛场开了一间专门做奶昔的餐馆。这个餐馆有N个位子(1<=N<=500000)排成一行,开始时,位子都是空的。
    每天,有M个不同的事件按次序发生(1<=M<=300000).事件分为两类:
    1.举办一个party,这个party有p头奶牛(1<=p<=N),这p头奶牛只会坐在相邻的位子。如果没有p个连续的空位,则奶牛们会离开。如果有多个,奶牛们会选择起点编号最小的一段空位。
    2.区间[a,b]的奶牛们离开座位。(1<=a

    输入

    第一行:两个整数N,M。
    第二行到第M+1行:每一行表示一个事件,它要么是形如“A p”,表示有一个party,这个party有p头奶牛;要么是形如L a b 的一行,表示区间[a,b]的所有奶牛全部离开。

    输出

    一行,表示不满足要求的聚会个数。

    样例输入

    10 4
    A 6
    L 2 4
    A 5
    A 2

    样例输出

    1

    解题报告:

    某天考试题。。。
    比较裸的线段树题目,关键在于代码实现有点难(考试时没调出来)
    用线段树控制区间的删除与增加,
    每个线段树上节点,维护3个变量le,max,re。

    struct Node{
        int l,r;
        int le,re,max;
        int lz;
    }nodes[MAXN*5];

    他们分别表示该区间内部靠左的空间大小,区间中不与左右空区间接触的最大空区间大小,该区间内部靠右的空区间大小。
    每次updata操作这样写:

    inline void updata(int u){
        nodes[u].max=maxf(nodes[u<<1].max,nodes[(u<<1)+1].max);
        nodes[u].max=maxf(nodes[u].max,nodes[u<<1].re+nodes[(u<<1)+1].le);
        le[u]=nodes[u<<1].le;
        re[u]=nodes[(u<<1)+1].re;
        if(nodes[u<<1].le==nodes[u<<1].r-nodes[u<<1].l+1)
            le[u]+=nodes[(u<<1)+1].le;
        if(nodes[(u<<1)+1].re==nodes[(u<<1)+1].r-nodes[(u<<1)+1].l+1)
            re[u]+=nodes[u<<1].re;
    }

    lazy标记保存三个值,非别是0,1,2
    代表无操作,填满和清空

    const int REMOVE=2;
    const int FILL=1;

    下传lazy时这么写:

    inline void pushdown(int index){
        nodes[index<<1].lz=nodes[(index<<1)+1].lz=nodes[index].lz;
        if(nodes[index].lz==FILL){
            nodes[index<<1].le=nodes[index<<1].re=nodes[index<<1].max=0;
            nodes[(index<<1)+1].le=nodes[(index<<1)+1].re=nodes[(index<<1)+1].max=0;
        }
        else if(nodes[index].lz==REMOVE){
            nodes[index<<1].max=nodes[index<<1].re=nodes[index<<1].le
            =nodes[index<<1].r-nodes[index<<1].l+1;
            nodes[(index<<1)+1].max=nodes[(index<<1)+1].re=nodes[(index<<1)+1].le
            =nodes[(index<<1)+1].r-nodes[(index<<1)+1].l+1;
        }
        nodes[index].lz=0;
    }

    由于每次A操作要寻找一段最靠左长度为len的空区间
    因此多添加一个函数:

    int len;
    int query(int u){
        if(nodes[u].l==nodes[u].r)return nodes[u].l;
        if(nodes[u].lz)
            pushdown(u);
        if(nodes[u<<1].max>=len)return query(u<<1);
        else if(nodes[u<<1].re+nodes[(u<<1)+1].le>=len)
            return nodes[u<<1].r-nodes[u<<1].re+1;
        else return query((u<<1)+1);
    }

    此函数用于寻找长度为len的空区间左端点位置
    原理:在保持当前区间中最大空区间长度大于len时尽量向左儿子前进,最后找到的一定是最靠左的区间。

    下面发AC代码:

    #include<cstdio>
    const int MAXN=510000;
    const int MAXM=310000;
    const int REMOVE=2;
    const int FILL=1;
    inline int max(const int &a,const int &b)
    {return a<b?b:a;}
    inline int min(const int &a,const int &b)
    {return a<b?a:b;}
    inline void getint(int &t){
        register char c;t=0;
        do{c=getchar();}while(c<'0'||c>'9');
        while(c<='9'&&c>='0'){t=t*10+c-'0';c=getchar();}
    }
    struct Node{
        int l,r;
        int le,re,max;
        int lz;
    }nodes[MAXN*5];
    inline void pushdown(int index){
        nodes[index<<1].lz=nodes[(index<<1)+1].lz=nodes[index].lz;
        if(nodes[index].lz==FILL){
            nodes[index<<1].le=nodes[index<<1].re=nodes[index<<1].max=0;
            nodes[(index<<1)+1].le=nodes[(index<<1)+1].re=nodes[(index<<1)+1].max=0;
        }
        else if(nodes[index].lz==REMOVE){
            nodes[index<<1].max=nodes[index<<1].re=nodes[index<<1].le
            =nodes[index<<1].r-nodes[index<<1].l+1;
            nodes[(index<<1)+1].max=nodes[(index<<1)+1].re=nodes[(index<<1)+1].le
            =nodes[(index<<1)+1].r-nodes[(index<<1)+1].l+1;
        }
        nodes[index].lz=0;
    }
    inline void updata(int u){
        nodes[u].max=max(nodes[u<<1].max,nodes[(u<<1)+1].max);
               nodes[u].max=max(nodes[u].max,nodes[u<<1].re+nodes[(u<<1)+1].le);
        nodes[u].le=nodes[u<<1].le;
        nodes[u].re=nodes[(u<<1)+1].re;
        if(nodes[u<<1].le==nodes[u<<1].r-nodes[u<<1].l+1)
            nodes[u].le+=nodes[(u<<1)+1].le;
        if(nodes[(u<<1)+1].re==nodes[(u<<1)+1].r-nodes[(u<<1)+1].l+1)
            nodes[u].re+=nodes[u<<1].re;
    }
    void build(int u,int l,int r){
        nodes[u].l=l;
        nodes[u].r=r;
        nodes[u].le=nodes[u].re=nodes[u].max=r-l+1;
        if(l==r)return ;
        int mid=(l+r)>>1;
        build(u<<1,l,mid);
        build((u<<1)+1,mid+1,r);
    }
    int l,r,k;
    void fill(int u){
        if(nodes[u].r<l||nodes[u].l>r)return ;
        if(nodes[u].lz)
            pushdown(u);
        if(nodes[u].l>=l&&nodes[u].r<=r){
            nodes[u].lz=k;
            if(k==REMOVE)nodes[u].le=nodes[u].re=nodes[u].max=nodes[u].r-nodes[u].l+1;
            else nodes[u].le=nodes[u].re=nodes[u].max=0;
            return ;
        }
        fill(u<<1);
        fill((u<<1)+1);
        updata(u);
    }
    int len;
    int query(int u){
        if(nodes[u].l==nodes[u].r)return nodes[u].l;
        if(nodes[u].lz)
            pushdown(u);
        if(nodes[u<<1].max>=len)return query(u<<1);
        else if(nodes[u<<1].re+nodes[(u<<1)+1].le>=len)
            return nodes[u<<1].r-nodes[u<<1].re+1;
        else return query((u<<1)+1);
    }
    int main(){
        int n,m;
        getint(n),getint(m);
        build(1,1,n);
        int ans=0;
        register char o;
        register int a,b,c;
        for(int i=1;i<=m;i++){
            while((o=getchar())!='L'&&o!='A');
            if(o=='L'){
                getint(a);
                getint(b);
                l=a;
                r=b;
                k=REMOVE;
                fill(1);
            }else {
                getint(c);
                if(c>nodes[1].max){ans++;continue;}
                len=c;
                int pos=query(1);
                l=pos;
                r=pos+c-1;
                k=FILL;
                fill(1);
            }
        }
        printf("%d
    ",ans);
    }
    //ORZ GJY LJH  TSY SYK YLY GMR YMX

    此题在bzoj上每个点给了10秒的时限,但是在学校oj上只给一秒。。。
    于是就成了卡常神题 = =

    我从两天前开始就是一阵狂T,把能想到的inline,读入优化,等等全部写上,再把函数参数全改成全局变量,
    结果是这样的:
    哎
    = =
    心酸呐= =
    然后只好在群里求助大神,大神说加个注释就能保证卡过!
    于是加上:

    //ORZ GJY LJH  TSY SYK YLY GMR YMX

    然后果然交了两发就过了啊哈哈哈

  • 相关阅读:
    【分享】64K视频合集
    【原译】四种方法统计字符串的行数&执行时间比较
    【原译】自动省略功能的WPF文本框控件
    【笔记】MD5加密
    【原译】在amazon kindle上安装Metasploit
    【笔记】wubi安装ubuntu遇到的问题
    【笔记】贪心算法找零钱(C#实现)
    ubuntu下在java代码中调用c代码
    sql截取字段最后以特定字符隔开的内容语句
    mysql中删除字符串或字段中空格函数
  • 原文地址:https://www.cnblogs.com/Hineven/p/5843565.html
Copyright © 2011-2022 走看看