题意:
n个花瓶,m个操作,花瓶里面有的有花,有的是空的。1操作是从a开始往右放b朵花,花瓶有了的不放,跳过,直到a右边都放满了花,多余的扔了。输出本次放花的起始位置,如果一朵不能放,输出一句话。
分析:
如果知道了区间的开始和结束位置的话,那么这道题就是非常简单的线段树了,那么重点就是怎么计算区间的开始和结束位置呢,因为题目给的是开始位置和往后放几个花的数量,那么就可以二分查找开始放的空位和结束放的空位,
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+10;
struct node{
int sum;
int mark;
}tree[maxn<<2];
int n,m;
void build(int l,int r,int i){
tree[i].sum=0;
tree[i].mark=-1;
if(l==r) return;
int mid=l+r>>1;
build(l,mid,i<<1);
build(mid+1,r,i<<1|1);
}
void update(int l,int r,int i){
if(tree[i].mark==-1) return;
int mid=l+r>>1;
if(tree[i].mark==1){
tree[i<<1].sum=mid-l+1;
tree[i<<1|1].sum=r-mid;
tree[i<<1].mark=tree[i<<1|1].mark=1;
}else{
tree[i<<1].sum=tree[i<<1|1].sum=tree[i<<1].mark=tree[i<<1|1].mark=0;
}
tree[i].mark=-1;
}
int query(int tl,int tr,int l,int r,int i){
if(tl>r||tr<l) return 0;
if(tl<=l&&r<=tr){
return tree[i].sum;
}
update(l,r,i);
int mid=l+r>>1;
return query(tl,tr,l,mid,i<<1)+query(tl,tr,mid+1,r,i<<1|1);
}
void change(int tl,int tr,int l,int r,int i,int flag){
if(tl>r||tr<l) return;
if(tl<=l&&r<=tr){
if(flag){
tree[i].sum=r-l+1;
tree[i].mark=1;
}else{
tree[i].sum=0;
tree[i].mark=0;
}
return;
}
update(l,r,i);
int mid=l+r>>1;
change(tl,tr,l,mid,i<<1,flag);
change(tl,tr,mid+1,r,i<<1|1,flag);
tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum;
}
int bin_search(int st,int x){
int l=st,r=n,ans=-1;
while(l<=r){
int mid=l+r>>1;
int tmp=query(st,mid,1,n,1);
if(tmp+x==mid-st+1){
ans=mid;
r=mid-1;
}else{
if(tmp+x>mid-st+1){
l=mid+1;
}else r=mid-1;
}
}
return ans;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--){
int f,l,r;
scanf("%d%d%d",&f,&l,&r);
if(f==1){
l++;
l=bin_search(l,1);
if(l==-1){
printf("Can not put any one.\n");
}else{
int tmp=query(l,n,1,n,1);
tmp=n-l+1-tmp;
if(tmp<=r) r=tmp;
r=bin_search(l,r);
printf("%d %d\n",l-1,r-1);
change(l,r,1,n,1,1);
}
}else{
l++,r++;
printf("%d\n",query(l,r,1,n,1));
change(l,r,1,n,1,0);
}
}
puts("");
}
}