题目
题意:一开始有标号为0~N-1的N个空花瓶,进行两种操作
操作1:A F,从标号为A的花瓶开始往后面放F朵花,如果当前花瓶有花了,就继续往后放,如果有花没有地方放了就丢掉。输出第一朵花和最后一朵花放的花瓶标号
操作2:A B,将花瓶A~B全部清空,并输出多少个花瓶被清空。
解法:
利用线段树的区间更新区间查询,线段树存储区间内有多少不为空的花瓶
对于操作1,先在[A,N-1]二分起点,再根据起点s二分[s,N-1]终点,要注意可能放不下所有的花的情况。
也可以在线段树上增加参数不用二分终点,会快很多。
代码:
#include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; #define maxn 50005 int n,m; int tree[maxn<<2],lazy[maxn<<2]; void down(int p,int la,int l,int r) { int mid=(l+r)>>1; if (la==-1) return; lazy[p<<1]=la; lazy[p<<1|1]=la; tree[p<<1]=la*(mid-l+1); tree[p<<1|1]=la*(r-mid); lazy[p]=-1; } void insert(int p,int L,int R,int l,int r,int flag) { if (l==L&&r==R) { tree[p]=flag*(R-L+1); lazy[p]=flag; return; } down(p,lazy[p],L,R); int mid=(L+R)>>1; if (r<=mid) insert(p<<1,L,mid,l,r,flag); else if (l>mid) insert(p<<1|1,mid+1,R,l,r,flag); else { insert(p<<1,L,mid,l,mid,flag); insert(p<<1|1,mid+1,R,mid+1,r,flag); } tree[p]=tree[p<<1]+tree[p<<1|1]; } int cal(int p,int L,int R,int l,int r) { if (l==L&&r==R) return tree[p]; down(p,lazy[p],L,R); int mid=(L+R)>>1,res; if (r<=mid) res=cal(p<<1,L,mid,l,r); else if (l>mid) res=cal(p<<1|1,mid+1,R,l,r); else res=cal(p<<1,L,mid,l,mid)+cal(p<<1|1,mid+1,R,mid+1,r); tree[p]=tree[p<<1]+tree[p<<1|1]; return res; } int main() { int cas,d,a,b; scanf("%d",&cas); while (cas--) { scanf("%d%d",&n,&m); memset(tree,0,sizeof(tree)); memset(lazy,-1,sizeof(lazy)); for (int i=1;i<=m;i++) { scanf("%d%d%d",&d,&a,&b); if (d==1) { int l=a+1,r=n; int s=-1,t=-1; while (l<=r) { int mid=(l+r)>>1; if (cal(1,1,n,a+1,mid)==mid-a) l=mid+1; else r=mid-1,s=mid; } if (s==-1){printf("Can not put any one.\n");continue;} l=s,r=n; b=min(b,n-s+1-cal(1,1,n,s,n)); while (l<=r) { int mid=(l+r)>>1; if (mid-s+1-cal(1,1,n,s,mid)>=b) t=mid,r=mid-1; else l=mid+1; } printf("%d %d\n",s-1,t-1); insert(1,1,n,s,t,1); } else { int tmp=cal(1,1,n,a+1,b+1); insert(1,1,n,a+1,b+1,0); printf("%d\n",tmp); } } printf("\n"); } return 0; }