关闭

【BZOJ3050】【USACO 2013 Jan Gold金组】坐座位 Seating

标签: ACM线段树线段树优化常数被卡怎么办
193人阅读 评论(1) 收藏 举报
分类:

【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\n",ans);
}
//ORZ GJY LJH  TSY SYK YLY GMR YMX

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

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

//ORZ GJY LJH  TSY SYK YLY GMR YMX

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

===============================分割线===============================
PS
据说这道题有华丽丽的常数超小不会被卡的差分做法

3
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:7920次
    • 积分:464
    • 等级:
    • 排名:千里之外
    • 原创:32篇
    • 转载:0篇
    • 译文:0篇
    • 评论:10条
    最新评论