题目大意:爱丽丝有N个花瓶,每个花瓶最多放一朵花。然后又如下两个操作。
1:A B 从第A个花瓶开始,往后依次插B朵花,直到不能插为止。如果一朵花都不能插入就输出“can.....”,否则输出第一个插花位置和最后一个插花位置。
2:A B 输出A B 之间有多少朵花 然后将这些花瓶清空。
当时比赛的时候始终找不到办法求出 最后一个插花位置。 也想过二分 但是想着怕效率太慢就没写。
其实最后想想也是 只有50000个操作 再乘以log N的二分 最多也就20W左右吧。
我是弱菜,自己写的程序效率巨慢。而且数组也开得多。
思路:
用lef 记录每个区间最左边可以放的花瓶,如果没有就是INF
用rig 记录每个区间最右边可以放的花瓶,如果没有就是-1
以上两个在更新的时候 lef选较小的 rig选较大的
cov表示区间全空 0 还是全满1 ,或者是不空不满 -1
然后emp表示区间空花瓶数。
主要的难点就是可以插花的时候怎么找到最后一个插花位置。
那么就用二分 找那个 emp == 所需要插花的数量 的最小右边的区间。
详见代码。
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <map> #include <set> #define maxn 50005 #define lson num<<1,s,mid #define rson num<<1|1,mid+1,e #define INF 0x3f3f3f3f using namespace std; int emp[maxn<<2]; int cov[maxn<<2]; int lef[maxn<<2]; int lzy[maxn<<2]; int rig[maxn<<2]; void pushup(int num) { emp[num]=emp[num<<1]+emp[num<<1|1]; if(cov[num<<1]==0 && cov[num<<1|1]==0)cov[num]==0; else if(cov[num<<1|1]==1 && cov[num<<1]==1)cov[num]=1; else cov[num]=-1; lef[num]=min(lef[num<<1],lef[num<<1|1]); rig[num]=max(rig[num<<1|1],rig[num<<1]); } void pushdown(int num,int s,int e) { if(lzy[num]!=0) { int mid=(s+e)>>1; lzy[num<<1]=lzy[num<<1|1]=lzy[num]; if(cov[num]!=-1)cov[num<<1]=cov[num<<1|1]=cov[num]; if(cov[num<<1]==0)emp[num<<1]=mid-s+1; else if(cov[num<<1]==1)emp[num<<1]=0; if(cov[num<<1|1]==0)emp[num<<1|1]=e-mid; else if(cov[num<<1|1]==1)emp[num<<1|1]=0; if(cov[num]==0) { lef[num<<1]=s; rig[num<<1]=mid; lef[num<<1|1]=mid+1; rig[num<<1|1]=e; } else { lef[num<<1]=lef[num<<1|1]=INF; rig[num<<1]=rig[num<<1|1]=-1; } lzy[num]=0; } } void build(int num,int s,int e) { cov[num]=0; lef[num]=s; rig[num]=e; emp[num]=e-s+1; lzy[num]=0; if(s==e)return; int mid=(s+e)>>1; build(lson); build(rson); pushup(num); } void update(int num,int s,int e,int l,int r,int val) { if(l<=s && r>=e) { lzy[num]=1; cov[num]=val; emp[num]=val==0?e-s+1:0; lef[num]=val==0?s:INF; rig[num]=val==0?e:-1; return; } pushdown(num,s,e); int mid=(s+e)>>1; if(l<=mid)update(lson,l,r,val); if(r>mid)update(rson,l,r,val); pushup(num); } int query(int num,int s,int e,int l,int r) { //printf("emp=%d,s=%d,e=%d,l=%d,r=%d,cov=%d\n",emp[num],s,e,l,r,cov[num]); if(l<=s && r>=e) { return emp[num]; } pushdown(num,s,e); int mid=(s+e)>>1; if(r<=mid)return query(lson,l,r); else if(l>mid)return query(rson,l,r); else return query(lson,l,mid)+query(rson,mid+1,r); pushup(num); } int Q_L(int num,int s,int e,int l,int r)//找到区间的LEF { if(l==s && r==e) { return lef[num]; } pushdown(num,s,e); int mid=(s+e)>>1; if(r<=mid)return Q_L(lson,l,r); else if(l>mid)return Q_L(rson,l,r); else return min(Q_L(lson,l,mid),Q_L(rson,mid+1,r)); pushup(num); } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); build(1,1,n);//额额额。 我是用1-N 建的树 而题目说的是 0-N-1 所以。。。后面有一些++操作。。。 int tl,tr; while(m--) { int op,aa,bb; scanf("%d%d%d",&op,&aa,&bb); if(op==1) { aa++; int ll=Q_L(1,1,n,aa,n);//找到左边 if(ll==INF) { printf("Can not put any one.\n"); continue; } printf("%d ",ll-1); int ttmp=query(1,1,n,ll,n); if(ttmp==0) printf("Can not put any one.\n"); else { if(ttmp<=bb) { printf("%d\n",rig[1]-1); update(1,1,n,ll,n,1); } else { int top=n,bot=ll,mid; while(bot<=top) { mid=(bot+top)>>1; int bin=query(1,1,n,ll,mid); if(bin>bb) top=mid-1; else if(bin<bb) bot=mid+1; else break; } while(query(1,1,n,ll,mid)==bb)//找到最小的 { mid--; if(mid<=ll)break; } printf("%d\n",mid+1-1); update(1,1,n,ll,mid+1,1); } } } else { aa++,bb++; printf("%d\n",bb-aa+1-query(1,1,n,aa,bb)); update(1,1,n,aa,bb,0); } } puts(""); } return 0; }