1.poj1823
题目大意
有一家酒店有n个房间,3种操作:1.从房间m开始连续有k个房间有人入住 2.从房间m开始连续空出k个房间 3.老板问你最长一段连续空房间的长度。
思路
在线段树上我们还要加一些东西,kl表示这个线段树上点表示的线段从左边开始的连续空房间数,kr表示这个线段树上点表示的线段从右边开始的连续空房间数,sum表示这一段里最大连续空房间数,这样我们就可以得到:sum[i]=max(r[i*2]+l[i*2+1],max(sum[i*2],sum[i*2+1]));
例子: 0 1 0 0 0 0 1 0 0这样一段,kl=1,kr=2,sum=4。
那么查询的时候直接输出sum[1]即可,很方便。
代码
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<climits>
using namespace std;
int n,m;
int kl[64001],kr[64001],sum[64001],laz[64001];
//laz:1表示全住,2表示全不住。
void pushdown(int s,int t,int i,int j){
int mid=(s+t)/2;
if(j==2){
kl[i*2]=mid-s+1;kl[i*2+1]=t-mid;
kr[i*2]=mid-s+1;kr[i*2+1]=t-mid;
sum[i*2]=mid-s+1;sum[i*2+1]=t-mid;
laz[i*2]=2;laz[i*2+1]=2;
}
else {
kl[i*2]=0;kl[i*2+1]=0;
kr[i*2]=0;kr[i*2+1]=0;
sum[i*2]=0;sum[i*2+1]=0;
laz[i*2]=1;laz[i*2+1]=1;
}
}
void chan(int l,int r,int s,int t,int i,int j){
int mid=(s+t)/2,tmp=0;
if(l<=s&&t<=r){
if(j==2){kl[i]=t-s+1;kr[i]=t-s+1;sum[i]=t-s+1;laz[i]=2;}
else {kl[i]=0;kr[i]=0;sum[i]=0;laz[i]=1;}
return;
}
if(s==t)return;
if(laz[i]>0)pushdown(s,t,i,laz[i]);
laz[i]=0;
if(l<=mid)chan(l,r,s,mid,i*2,j);
if(mid+1<=r)chan(l,r,mid+1,t,i*2+1,j);
kl[i]=kl[i*2];
if(kl[i*2]==mid-s+1)kl[i]+=kl[i*2+1];
kr[i]=kr[i*2+1];
if(kr[i*2+1]==t-mid)kr[i]+=kr[i*2];
sum[i]=max(kr[i*2]+kl[i*2+1],max(sum[i*2],sum[i*2+1]));
}
int main()
{
int i,j,x,l,r;
scanf("%d%d",&n,&m);
laz[1]=2;kl[1]=n;kr[1]=n;sum[1]=n;
for(i=1;i<=m;i++){
scanf("%d",&x);
if(x==1){scanf("%d%d",&l,&r);r=l+r-1;chan(l,r,1,n,1,1);}
else if(x==2){scanf("%d%d",&l,&r);r=l+r-1;chan(l,r,1,n,1,2);}
else if(x==3)printf("%d\n",sum[1]);
}
return 0;
}
2.poj3667
题目描述
有一家酒店有n个房间,有2种操作:1.找到左端点最小的一段长度为m连续空房间入住旅客,并输出左端点。2.空出一段左端点为m长度为k的房间
思路
和上一题十分相似,就是查询操作麻烦一些。先找左子树,再看中间这一段是不是可行,如果可行,把这一段住满,直接返回。最后找右子树。
代码
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<climits>
using namespace std;
int n,m;
int sum[200001],kl[200001],kr[200001],laz[200001];
//1:表示全空,2表示全不空
void pushdown(int s,int t,int i,int bb){//lazy标记下传
int mid=(s+t)/2;
if(bb==1){
sum[i*2]=mid-s+1;sum[i*2+1]=t-mid;
kl[i*2]=mid-s+1;kl[i*2+1]=t-mid;
kr[i*2]=mid-s+1;kr[i*2+1]=t-mid;
laz[i*2]=1;laz[i*2+1]=1;
}
else {
sum[i*2]=0;sum[i*2+1]=0;
kl[i*2]=0;kl[i*2+1]=0;
kr[i*2]=0;kr[i*2+1]=0;
laz[i*2]=2;laz[i*2+1]=2;
}
laz[i]=0;
}
void emp(int l,int r,int s,int t,int i){//空出
if(l<=s&&t<=r){laz[i]=1;sum[i]=t-s+1;kl[i]=t-s+1;kr[i]=t-s+1;return;}
if(s==t)return;
int mid=(s+t)/2;
if(laz[i]>0)pushdown(s,t,i,laz[i]);
if(l<=mid)emp(l,r,s,mid,i*2);
if(mid+1<=r)emp(l,r,mid+1,t,i*2+1);
kl[i]=kl[i*2];
if(kl[i*2]==mid-s+1)kl[i]+=kl[i*2+1];
kr[i]=kr[i*2+1];
if(kr[i*2+1]==t-mid)kr[i]+=kr[i*2];
sum[i]=max(kr[i*2]+kl[i*2+1],max(sum[i*2],sum[i*2+1]));
}
void init(int l,int r,int s,int t,int i){//入住
if(l<=s&&t<=r){laz[i]=2;sum[i]=0;kl[i]=0;kr[i]=0;return;}
if(s==t)return;
int mid=(s+t)/2;
if(laz[i]>0)pushdown(s,t,i,laz[i]);
if(l<=mid)init(l,r,s,mid,i*2);
if(mid+1<=r)init(l,r,mid+1,t,i*2+1);
kl[i]=kl[i*2];
if(kl[i*2]==mid-s+1)kl[i]+=kl[i*2+1];
kr[i]=kr[i*2+1];
if(kr[i*2+1]==t-mid)kr[i]+=kr[i*2];
sum[i]=max(kr[i*2]+kl[i*2+1],max(sum[i*2],sum[i*2+1]));
}
int find(int num,int s,int t,int i){//寻找区间
if(sum[i]<num)return 0;
int mid=(s+t)/2,ss,tt;
if(laz[i]>0)pushdown(s,t,i,laz[i]);
if(sum[i*2]>=num)return find(num,s,mid,i*2);//找左子树
if(kr[i*2]+kl[i*2+1]>=num){//找中间
ss=mid-kr[i*2]+1;tt=ss+num-1;
init(ss,tt,1,n,1);//入住
return ss;
}
if(sum[i*2+1]>=num)return find(num,mid+1,t,i*2+1);//找右子树
return 0;
}
int main()
{
int i,j,x,y,z;
scanf("%d%d",&n,&m);
sum[1]=n;kl[1]=n;kr[1]=n;laz[1]=1;
for(i=1;i<=m;i++){
scanf("%d",&x);
if(x==1){
scanf("%d",&y);printf("%d\n",find(y,1,n,1));
}
else {scanf("%d%d",&y,&z);z=y+z-1;emp(y,z,1,n,1);}
}
return 0;
}