关闭

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

标签: ACM线段树线段树优化常数被卡怎么办
422人阅读 评论(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
查看评论

P2052【USACO 2013 January Gold】座位

问题描述奶牛们开了一家餐馆。该餐馆里有N(1 <= N <= 500,000)个排成一列的座位(编号1到N),编号越小的座位越靠近窗户。早晨开业时,座位都是空的。今天餐馆里发生了M(1 <= M <= 300,000)个事件,这些事件总共分两类: 1.一伙人一起来就餐,该伙...
  • nkliuorz
  • nkliuorz
  • 2017-07-25 12:25
  • 97

【USACO 2013 January Gold】座位 --线段树

一类线段树的典型代表
  • u012274244
  • u012274244
  • 2013-12-20 14:19
  • 1202

线段树--hotel usaco feb08gold

【Usaco Feb08 Gold】旅馆 Time Limit:10000MS  Memory Limit:65536K Case Time Limit:1000MS Description 奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光。作为整个旅游的...
  • INCINCIBLE
  • INCINCIBLE
  • 2016-07-12 16:36
  • 317

【POJ3658】【USACO 2008 Jan Gold】 2.Artificial Lake人工湖 单调栈

水题,除草,勿看。
  • Vmurder
  • Vmurder
  • 2014-12-30 17:47
  • 1477

[BZOJ1596][Usaco2008 Jan]电话网络(贪心||树形dp)

我们目送着那消失的飞空机云 因为太过於炫丽而想逃开 不知何时变的如此的软弱
  • Clove_unique
  • Clove_unique
  • 2016-04-26 08:10
  • 639

NKOJ 3213 牧草鉴赏家(Tarjan缩点+最长路)

P3213【USACO 2015 Jan Gold】牧草鉴赏家问题描述约翰有n块草场,编号1到n,这些草场由若干条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。贝西总是从1号草场出发,最后回到1号草场。她想经过尽可能多的草场,贝西在通一个草场只吃一次草,所以一个草场可以经...
  • Mogician_Evian
  • Mogician_Evian
  • 2017-09-10 15:04
  • 146

USACO2018JAN Gold

A.mootube(离线+并查集) 给定一棵n个点的树(n=1e5),有边权,两点间距离定义为两点路径上的边权最小值。m个询问(m=1e5),k,v,询问对于点v,距离>=k的点有多少个(不含v)离线+并查集,按k从大到小做,此时与v连通的所有点均为答案。 我已经菜到想不出这种题了qaqB...
  • Icefox_zhx
  • Icefox_zhx
  • 2018-01-27 00:26
  • 88

【USACO 2013 January Gold】奶牛排队

Description 农夫约翰的N(1 约翰觉得如果连续排列的一段奶牛有相同的血统编号的话,奶牛们看起来会更具有威猛。为了创造这样的连续段,约翰最多能选出k种血统的奶牛,并把他们全部从队列中赶走。请帮助约翰计算这样做能得到的由相同血统编号的牛构成的连续段的长度最大是多少? Input 第一行,两...
  • u012274244
  • u012274244
  • 2013-12-18 14:46
  • 1619

[BZOJ3050][Usaco2013 Jan]Seating(线段树)

最可耻的诬陷,总是彬彬有礼。
  • Clove_unique
  • Clove_unique
  • 2016-03-31 23:33
  • 639

【POJ3657】【USACO 2008 Jan Gold】 1.Haybale Guessing 二分答案,并查集check

题意: 输入n、m表示数列长度为n,有m条有序的限制{l,r,x}。 限制:l~r间所有数最小值为x。 问到第几条限制开始出现矛盾,都不出现输出"0"。 题解: 首先这题比较厉害,正常解有点难,不妨转化成二分答案。 我们二分“答案”,也就是第ans条出现矛盾。...
  • Vmurder
  • Vmurder
  • 2014-12-23 17:46
  • 1774
    个人资料
    • 访问:15201次
    • 积分:533
    • 等级:
    • 排名:千里之外
    • 原创:32篇
    • 转载:0篇
    • 译文:0篇
    • 评论:10条
    最新评论