Vases and Flowers(线段树+二分)
Vases and Flowers(线段树+二分)
解法一:
二分区间,线段区间查询空花瓶个数。复杂度\(O(nlogn^2)\)
由于区间越大,能空花瓶数越多,满足二分单调性。
操作1:二分pos到n区间的,区间查询二分区间的空花瓶数,找到插花的第一个位置和最后一个位置,然后区间修改
操作2:输出查询区间值,修改区间。
#include <iostream>
#include <cstring>
#define lch 2*k
#define rch 2*k+1
#define mid (l+r)/2
using namespace std;
const int N=1e5+7;
int t;
int n,m,tree[4*N],tap[4*N];
void init(int k,int l,int r){
tap[k]=-1;
if(l>=r){
tree[k]=0;
return;
}
init(lch,l,mid);
init(rch,mid+1,r);
tree[k]=tree[lch]+tree[rch];
}
void pushdown(int k,int zl,int zr,int yl,int yr){
if(tap[k]!=-1){
tree[lch]=tap[k]*(zr-zl+1);
tree[rch]=tap[k]*(yr-yl+1);
tap[lch]=tap[k];
tap[rch]=tap[k];
tap[k]=-1;
}
}
void update(int k,int l,int r,int ql,int qr,int cost){
if(ql>qr){
return;
}
if(ql<=l&&r<=qr){
tree[k]=cost*(r-l+1);
tap[k]=cost;
return;
}
pushdown(k,l,mid,mid+1,r);
if(ql<=mid) update(lch,l,mid,ql,qr,cost);
if(mid+1<=qr) update(rch,mid+1,r,ql,qr,cost);
tree[k]=tree[lch]+tree[rch];
}
int qurey(int k,int l,int r,int ql,int qr){
if(ql>qr){
return 0;
}
if(ql<=l&&r<=qr){
return tree[k];
}
pushdown(k,l,mid,mid+1,r);
int sum1=0,sum2=0;
if(ql<=mid) sum1=qurey(lch,l,mid,ql,qr);
if(mid+1<=qr) sum2=qurey(rch,mid+1,r,ql,qr);
return sum1+sum2;
}
bool judge(int ql,int qr,int key){
int cnt=(qr-ql+1)-qurey(1,1,n,ql,qr);
if(cnt>=key){
return true;
}else{
return false;
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
//memset(tree,0,sizeof(tree));
//memset(tap,-1,sizeof(tap));
init(1,1,n);
while(m--){
int op,x,y;
scanf("%d %d %d",&op,&x,&y);
if(op==1){
y=min(y,(n-x)-qurey(1,1,n,x+1,n));
int jl=x+1,jr=n;
while(jl<=jr){
int md=(jl+jr)/2;
if(judge(x+1,md,y)){
jr=md-1;
}else{
jl=md+1;
}
}
int ll=x+1,rr=jl;
while(ll<=rr){
int md=(ll+rr)/2;
if(judge(md,jl,y)){
ll=md+1;
}else{
rr=md-1;
}
}
if(y==0){
printf("Can not put any one.\n");
}else{
update(1,1,n,rr,jl,1);
printf("%d %d\n",rr-1,jl-1);
}
}else if(op==2){
printf("%d\n",qurey(1,1,n,x+1,y+1));
update(1,1,n,x+1,y+1,0);
}
}
printf("\n");
}
}
解法二:
线段树上二分,复杂度\(O(nlogn)\)
由题意可知,我们需要往\(pos\)位置插b朵花,也可能插少于\(b\)朵花,即为b朵花和\(pos\)到\(n\)位置的空花瓶数取最小值。
操作1:
为了方便从\(1\)到\(n\)线段树二分,我们查询\(1\)到\(pos-1\)位置的空花瓶数,加上\(pos\)位置之后的需要插花的数量,设为\(cnt1\)。
在区间1到n线段树上二分查找从1开始到第cnt1空花瓶位置,设左子树的空花瓶数为\(tem\),若\(tem\)数大于或等于\(cnt1\),则往左子树查找,否则往右子树查找\(cnt1-tem\),找到第\(cnt1\)位置的空花瓶的下标值,即为最后插花的位置。
查询开始插花的位置,即查询\(pos\)到\(n\)的空花瓶数,为\(cnt2\),同理二分查找\(n\)开始到\(cnt2\)个空花瓶位置,即为开始插花的位置。
最后区间修改。
操作2:
区间查询和修改。
#include <iostream>
#define lch 2*k
#define rch 2*k+1
#define mid (l+r)/2
using namespace std;
const int N=1e5+7;
int tree[4*N],tap[4*N];
int t,n,q;
void init(int k,int l,int r){
tap[k]=-1;
if(l>=r){
tree[k]=1;
return;
}
init(lch,l,mid);
init(rch,mid+1,r);
tree[k]=tree[lch]+tree[rch];
}
void pushdown(int k,int zl,int zr,int yl,int yr){
if(tap[k]!=-1){
tree[lch]=tap[k]*(zr-zl+1);
tree[rch]=tap[k]*(yr-yl+1);
tap[lch]=tap[k];
tap[rch]=tap[k];
tap[k]=-1;
}
}
void update(int k,int l,int r,int ql,int qr,int cost){
if(ql>qr){
return;
}
if(ql<=l&&r<=qr){
tree[k]=cost*(r-l+1);
tap[k]=cost;
return;
}
pushdown(k,l,mid,mid+1,r);
if(ql<=mid) update(lch,l,mid,ql,qr,cost);
if(mid+1<=qr) update(rch,mid+1,r,ql,qr,cost);
tree[k]=tree[lch]+tree[rch];
}
int qurey(int k,int l,int r,int ql,int qr){
if(ql>qr){
return 0;
}
if(ql<=l&&r<=qr){
return tree[k];
}
pushdown(k,l,mid,mid+1,r);
int sum1=0,sum2=0;
if(ql<=mid) sum1=qurey(lch,l,mid,ql,qr);
if(mid+1<=qr) sum2=qurey(rch,mid+1,r,ql,qr);
return sum1+sum2;
}
int searchR(int k,int l,int r,int cnt){
if(l>=r){
return l;
}
pushdown(k,l,mid,mid+1,r);
int tem=tree[lch];
int inx=-1;
if(tem>=cnt){
inx=searchR(lch,l,mid,cnt);
}else{
inx=searchR(rch,mid+1,r,cnt-tem);
}
return inx;
}
int searchL(int k,int l,int r,int cnt){
if(l>=r){
return l;
}
pushdown(k,l,mid,mid+1,r);
int tem=tree[rch];
int inx=-1;
if(tem>=cnt){
inx=searchL(rch,mid+1,r,cnt);
}else{
inx=searchL(lch,l,mid,cnt-tem);
}
return inx;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&q);
init(1,1,n);
while(q--){
int op,a,b;
scanf("%d %d %d",&op,&a,&b);
if(op==1){
if(qurey(1,1,n,a+1,n)==0){
printf("Can not put any one.\n");
continue;
}
int cnt1=min(qurey(1,1,n,1,n),b+qurey(1,1,n,1,a));
int inx1=searchR(1,1,n,cnt1);
int cnt2=qurey(1,1,n,a+1,n);
int inx2=searchL(1,1,n,cnt2);
update(1,1,n,inx2,inx1,0);
printf("%d %d\n",inx2-1,inx1-1);
}
else if(op==2){
printf("%d\n",(b-a+1)-qurey(1,1,n,a+1,b+1));
update(1,1,n,a+1,b+1,1);
}
}
printf("\n");
}
}