HDU 4614 Vases and Flowers 【线段树】+【二分】

<题目链接>

题目大意:

有n个花瓶,每个花瓶中只能放一朵花。两种操作,一种是从A开始放F朵花,如果有的花瓶中已经有花则跳过这个花瓶,往下一个花瓶放;第二种是将区间[A,B]之间花瓶中的花清空。如果是第一种操作,输出这次放的花的左右端点;如果是第二种操作,输出这次总共清理出了多少支花。

解题分析:

本题可以很巧妙的将这两种操作全部转化为区间修改,毫无疑问,第二种操作肯定是区间修改;对于第一种操作,可以用二分答案将x后的第num个空瓶的坐标查找出来,因为二分答案时需要直接查询指定区间内空瓶的数量,所以线段树叶子节点维护一个值,代表该区间空瓶的数量,得到第一个和最后一个空瓶的数量后,直接对该区间进行区间修改,将空瓶数量全部置0即可。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 #define Lson rt<<1,l,mid
 7 #define Rson rt<<1|1,mid+1,r
 8 const int M =5e4+5;
 9 int n,m;
10 int tr[M<<2],lazy[M<<2];
11 //lazy[rt]初始化为-1,lazy[rt]==0,代表这个区间瓶子全满,lazy[rt]==1,代表这个区间瓶子全空
12 void Pushup(int rt){ tr[rt]=tr[rt<<1]+tr[rt<<1|1]; }
13 void Pushdown(int rt,int len){
14     if(lazy[rt]!=-1){
15         int tmp=lazy[rt];
16         lazy[rt<<1]=tmp;
17         lazy[rt<<1|1]=tmp;
18         tr[rt<<1]=(len-(len>>1))*tmp;
19         tr[rt<<1|1]=(len>>1)*tmp;
20         lazy[rt]=-1;
21     }
22 }
23 void build(int rt,int l,int r){
24     lazy[rt]=-1;
25     if(l==r){
26         tr[rt]=1;
27         return;
28     }
29     int mid=(l+r)>>1;
30     build(Lson);
31     build(Rson);
32     Pushup(rt);
33 }
34 void update(int rt,int l,int r,int L,int R,int c){
35     if(L<=l&&r<=R){
36         lazy[rt]=c;
37         tr[rt]=(r-l+1)*c;
38         return;
39     }
40     Pushdown(rt,r-l+1);
41     int mid=(l+r)>>1;
42     if(L<=mid)update(Lson,L,R,c);
43     if(R>mid)update(Rson,L,R,c);
44     Pushup(rt);
45 }
46 int query(int rt,int l,int r,int L,int R){    //查询指定区间的空瓶数量
47     if(L<=l&&r<=R)return tr[rt];
48     Pushdown(rt,r-l+1);
49     int mid=(l+r)>>1;
50     int ans=0;
51     if(L<=mid)ans+=query(Lson,L,R);
52     if(R>mid)ans+=query(Rson,L,R);
53     return ans;
54 }
55 int bin_search(int x,int num){     //二分答案,查找从x开始的第num个空瓶子的下标
56     int l=x,r=n,ans;
57     while(l<=r){
58         int mid=(l+r)>>1;
59         if(query(1,1,n,x,mid)>=num)ans=mid,r=mid-1;
60         else l=mid+1;
61     }
62     return ans;
63 }
64 int main(){
65     int T;scanf("%d",&T);
66     while(T--){
67         scanf("%d%d",&n,&m);
68         build(1,1,n);
69         while(m--){
70             int op,x,y;
71             scanf("%d%d%d",&op,&x,&y);
72             if(op==1){   
73                 x++;  //由于线段树叶子节点习惯从1~n开始存储,所以a++
74                 int cnt=query(1,1,n,x,n);  //查询这个区间的空瓶子数量
75                 if(cnt==0)printf("Can not put any one.
");
76                 else{  //二分查找第一个空瓶子和最后一个空瓶子的下标
77                     int le=bin_search(x,1);
78                     int ri=bin_search(x,min(cnt,y));
79                     update(1,1,n,le,ri,0);
80                     printf("%d %d
",le-1,ri-1);
81                 }
82             }
83             else{
84                 x++,y++;
85                 printf("%d
",y-x+1-query(1,1,n,x,y));
86                 update(1,1,n,x,y,1);
87             }
88         }
89         printf("
");
90     }
91     return 0;
92 }

2018-09-29