代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 55555 ; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 int num[MAXN<<2],dly[MAXN<<2]; void push_up(int rt) { num[rt] = num[rt<<1]+num[rt<<1|1]; } void push_down(int rt,int l,int r,int m) { num[rt<<1] = dly[rt]*(m-l+1); num[rt<<1|1] = dly[rt]*(r-m); dly[rt<<1] = dly[rt<<1|1] =dly[rt]; dly[rt] = -1; } void build(int l,int r,int rt) { dly[rt] = -1; if(l==r) { num[rt] = 1 ; return ; } int m = l+r>>1; build(lson); build(rson); push_up(rt); } int query(int l,int r,int rt,int a,int b) { if(a<=l&&b>=r) { return num[rt]; } int m = l+r>>1; if(dly[rt]!=-1) push_down(rt,l,r,m); int cnt1 = 0,cnt2 = 0; if(a<=m) cnt1 = query(lson,a,b); if(b>m) cnt2 = query(rson,a,b); return cnt1+cnt2; } void update(int l,int r,int rt,int a,int b,int c) { if(a<=l&&b>=r) { dly[rt] = c; num[rt] = c*(r-l+1); //printf("l = %d,r = %d,rt = %d,a = %d,b = %d,c = %d,num = %d\n",l,r,rt,a,b,c,num[rt]); return ; } int m = l+r>>1; if(dly[rt]!=-1) { push_down(rt,l,r,m); } if(a<=m) update(lson,a,b,c); if(b>m) update(rson,a,b,c); push_up(rt); } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); build(0,n-1,1); int flag,a,b; for(int i=0;i<m;i++) { scanf("%d%d%d",&flag,&a,&b); if(flag==1) { int s = query(0,n-1,1,a,n-1); if(s==0) puts("Can not put any one."); else { int ll,rr; if(s<=b) { b=s; } int l = a,r = n-1; while(l<r) { int m = l+r>>1; int tmp = query(0,n-1,1,a,m); //printf("l = %d,r = %d,m = %d,tmp = %d\n",l,r,m,tmp); if(tmp==b) r = m; else if(tmp>b) r = m-1; else l = m+1; } rr = l; l = a,r = rr; while(l<r) { int m = (l+r+1)>>1; int tmp = query(0,n-1,1,m,rr); //printf("l = %d,r = %d,m = %d,tmp = %d\n",l,r,m,tmp); if(tmp==b) l = m; else if(tmp>b) l = m+1; else r = m-1; } ll = l; printf("%d %d\n",ll,rr); update(0,n-1,1,ll,rr,0); } } else { printf("%d\n",b-a+1-query(0,n-1,1,a,b)); update(0,n-1,1,a,b,1); } } puts(""); } return 0; }
上面那个是用二分的,复杂度上多了个 logn,后来问了下其他人,这道题也可以不用二分,直接用纯线段树的方法做,本来以为找位子会很麻烦,可是经过他们点拨之后,其实也感觉挺简单的,只能说自己的线段树功力还不够吧。就是先把0到一开始尝试放的位子之前有几个空花瓶算出来,记为s,然后找和为s+1的区间,这就是l,然后再找和为s+min(tot,c)(tot为一开始尝试放的位子到n-1的总共空花瓶数,c为放花数),这就是r,然后再更新就好了。个人感觉,把开始放花的边界拓展为0是关键。
具体过程看代码吧,代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 const int MAXN = 50001 ; int cnt[MAXN<<2],dly[MAXN<<2]; void push_up(int rt) { cnt[rt] = cnt[rt<<1]+cnt[rt<<1|1]; } void build(int l,int r,int rt) { dly[rt] = -1; if(l==r) { cnt[rt] = 1; return ; } int m = l+r>>1; build(lson); build(rson); push_up(rt); } void push_down(int rt,int l,int r,int m) { cnt[rt<<1] = dly[rt]*(m-l+1); cnt[rt<<1|1] = dly[rt]*(r-m); dly[rt<<1] = dly[rt<<1|1] =dly[rt]; dly[rt] = -1; } void update(int l,int r,int rt,int a,int b,int c) { if(a<=l&&b>=r) { dly[rt] = c; cnt[rt] = c*(r-l+1); return ; } int m = l+r>>1; if(dly[rt]!=-1) push_down(rt,l,r,m); if(a<=m) update(lson,a,b,c); if(b>m) update(rson,a,b,c); push_up(rt); } int query1(int l,int r,int rt,int a,int b) { if(a<=l&&b>=r) { return cnt[rt]; } int m = l+r>>1; if(dly[rt]!=-1) push_down(rt,l,r,m); int cnt1 = 0,cnt2 = 0; if(a<=m) cnt1 = query1(lson,a,b); if(b>m) cnt2 = query1(rson,a,b); return cnt1+cnt2; } int query2(int l,int r,int rt,int c) { if(l==r) return l; int m = l+r>>1; if(dly[rt]!=-1) push_down(rt,l,r,m); if(cnt[rt<<1]<c) { return query2(rson,c-cnt[rt<<1]); } else return query2(lson,c); } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); build(0,n-1,1); int a,b,c; while(m--) { scanf("%d%d%d",&a,&b,&c); if(a==1) { int tot = query1(0,n-1,1,b,n-1); if(tot==0) { puts("Can not put any one."); continue; } c = min(c,tot); int s; if(b==0) s=0; else s = query1(0,n-1,1,0,b-1); int l = query2(0,n-1,1,s+1); int r = query2(0,n-1,1,s+c); printf("%d %d\n",l,r); update(0,n-1,1,l,r,0); } else { printf("%d\n",c-b+1-query1(0,n-1,1,b,c)); update(0,n-1,1,b,c,1); } } puts(""); } return 0; }