您的位置:首页 > 其它

bzoj 3050: [Usaco2013 Jan] Seating

2016-03-30 08:12 369 查看
Description

To earn some extra money, the cows have opened a restaurant in their barn specializing in milkshakes. The restaurant has N seats (1 <= N <= 500,000) in a row. Initially, they are all empty. Throughout the day, there are M different events that happen in sequence at the restaurant (1 <= M <= 300,000). The two types of events that can happen are: 1. A party of size p arrives (1 <= p <= N). Bessie wants to seat the party in a contiguous block of p empty seats. If this is possible, she does so in the lowest position possible in the list of seats. If it is impossible, the party is turned away. 2. A range [a,b] is given (1 <= a <= b <= N), and everybody in that range of seats leaves. Please help Bessie count the total number of parties that are turned away over the course of the day.

m(m<=300,000)个操作。操作分2种:

1.A p,表示把编号最小的空着的长度为p的区间图上颜色。

2.L a b,表示把从a到b的区间(包括端点)全部擦干净(没颜色还是没颜色)。

Q:有多少个操作1不能实现?

Input

Line 1: Two space-separated integers, N and M.

Lines 2..M+1: Each line describes a single event. It is either a line of the form “A p” (meaning a party of size p arrives) or “L a b” (meaning that all cows in the range [a, b] leave).

题目大意就是给定一段长度为n的01序列,初始时全是0,然后有m个操作:

- A p 表示在序列内找到一段长度为p的全是0的区间并将其变成1,若有多个区间符合条件则修改靠前的区间,如果没有符合条件的区间则答案加一;

- L [l,r] 表示将区间[l,r]全部变为0;

本题要维护区间,修改区间值,那么就考虑线段树。修改比较简单,是线段树的模板操作,在所要修改的区间的根节点打标记即可;而判断是否有符合条件的区间,则需要一些附加操作。对于线段树的每一个节点,维护其子树中值为0的最长前缀长度,值为0的最长后缀长度,以及值为0的最长区间长度。那么查询时,判断根结点的最长连续区间长度是否大于p,若小于p则ans+1,否则就向下查询,如果左子树中的最长区间符合条件,就递归到左子树,如果左子树后缀加右子树前缀符合条件,则查询区间,并将其赋值为1,同时更新路径上的点;否则递归到右子树;

void insert(int now,int l,int r,int p) {
if(p==0) return;
int c,h;c=t[now].ch[0];h=t[now].ch[1];
int mid=l+r>>1;
if(c&&t[c].max_sum>=p) insert(c,l,mid,p);
else if(t[c].sur+t[h].pre>=p){
if(p==1) {
t[now].full=1;
markdown(now);markdown(c);markdown(h);
return;
}
int a=mid-t[c].sur+1,b=mid+(p-t[c].sur);
find(now,l,r,a,b,1);
}
else if(h&&t[h].max_sum>=p) insert(h,mid+1,r,p);
update(now);
}


t[now]表示当前节点,t[now].max_sum表示该子树中的最长连续为0区间的长度;find(now,l,r,a,b,1)表示在区间为[l,r]的子树now中找区间[a,b]并将其赋值为1;

本题与另一道题很像 在我的另一篇博文里有更详细的讲解和更多操作的实现,是用Splay实现的。有兴趣的可以研究一下。

戳这里->bzoj 1500 题解

下面是本题代码(程序有些慢,,大概7000毫秒左右。):

#include<iostream>
#include<cstdio>
using namespace std;
struct node {
int ch[2],v,full,max_sum,pre,sur,sum;
node() { ch[0]=ch[1]=full=max_sum=sum=v=pre=sur=0; }
}t[1500010];
int n,m,head,ans;
void markdown(int now) {
if(t[now].full) {
t[now].full=0; t[now].max_sum=t[now].pre=t[now].sur=0;
if(t[now].ch[0]) t[now*2].full=1,t[now*2].v=0;
if(t[now].ch[1]) t[now*2+1].full=1,t[now*2+1].v=0;
t[now].pre=t[now].sur=t[now].max_sum=0;
}
if(t[now].v) {
t[now].v=0; t[now].max_sum=t[now].pre=t[now].sur=t[now].sum;
if(t[now].ch[0]) t[now*2].v=1,t[now*2].full=0;
if(t[now].ch[1]) t[now*2+1].v=1,t[now*2+1].full=0;
}
}
void update(int now) {
markdown(now);
if(!t[now].ch[0]&&!t[now].ch[1]) return;
markdown(now*2); markdown(now*2+1);
t[now].pre=t[now*2].pre; t[now].sur=t[now*2+1].sur;
t[now].max_sum=max(t[now*2+1].max_sum,t[now*2].max_sum);
if(t[now*2].pre==t[now*2].sum)
t[now].pre=t[now*2].pre+t[now*2+1].pre;
if(t[now*2+1].sur==t[now*2+1].sum)
t[now].sur=t[now*2+1].sur+t[now*2].sur;
t[now].max_sum=max(t[now].max_sum,t[now*2].sur+t[now*2+1].pre);
}
void build(int now,int l,int r) {
if(l==r) {
t[now].sum=t[now].pre=t[now].sur=t[now].max_sum=1;return;
}
int mid=l+r>>1;
t[now].ch[0]=now*2; t[now].ch[1]=now*2+1;
build(now*2,l,mid); build(now*2+1,mid+1,r);
t[now].sum=t[now*2].sum+t[now*2+1].sum;
t[now].pre=t[now].sur=t[now].max_sum=t[now].sum;
}
void find(int now,int l,int r,int a,int b,int d) {
markdown(now);
if(l>=a&&r<=b) {
if(d) t[now].full=1;
else t[now].v=1;
markdown(now); return;
}
int mid=l+r>>1;
if(a<=mid) find(now*2,l,mid,a,b,d);
if(b>mid) find(now*2+1,mid+1,r,a,b,d);
update(now);
return;
}
void insert(int now,int l,int r,int p) { if(p==0) return; int c,h;c=t[now].ch[0];h=t[now].ch[1]; int mid=l+r>>1; if(c&&t[c].max_sum>=p) insert(c,l,mid,p); else if(t[c].sur+t[h].pre>=p){ if(p==1) { t[now].full=1; markdown(now);markdown(c);markdown(h); return; } int a=mid-t[c].sur+1,b=mid+(p-t[c].sur); find(now,l,r,a,b,1); } else if(h&&t[h].max_sum>=p) insert(h,mid+1,r,p); update(now); }
int in() {
int s=0,v=0;char c;
while((c=getchar())<'0'||c>'9') if(c=='-') v=1;s=c-'0';
while((c=getchar())>='0'&&c<='9') s=s*10+c-'0';
return v?-s:s;
}
int main() {
n=in();m=in();
head=1; build(head,1,n);
for(int i=1;i<=m;i++) {
char z;while((z=getchar())!='A'&&z!='L');
if(z=='A') {
int p=in(); if(t[head].max_sum>=p) insert(head,1,n,p); else ans++;
}
else {
int a=in(),b=in();
find(head,1,n,a,b,0);
}
} printf("%d",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: