HDU 4614 二分区间端点实现线段树区间查询

Vases and Flowers 

 Alice is so popular that she can receive many flowers everyday. She has N vases numbered from 0 to N-1. When she receive some flowers, she will try to put them in the vases, one flower in one vase. She randomly choose the vase A and try to put a flower in the vase. If the there is no flower in the vase, she will put a flower in it, otherwise she skip this vase. And then she will try put in the vase A+1, A+2, ..., N-1, until there is no flower left or she has tried the vase N-1. The left flowers will be discarded. Of course, sometimes she will clean the vases. Because there are too many vases, she randomly choose to clean the vases numbered from A to B(A <= B). The flowers in the cleaned vases will be discarded.

Input

  The first line contains an integer T, indicating the number of test cases. 
  For each test case, the first line contains two integers N(1 < N < 50001) and M(1 < M < 50001). N is the number of vases, and M is the operations of Alice. Each of the next M lines contains three integers. The first integer of one line is K(1 or 2). If K is 1, then two integers A and F follow. It means Alice receive F flowers and try to put a flower in the vase A first. If K is 2, then two integers A and B follow. It means the owner would like to clean the vases numbered from A to B(A <= B).

Output

  For each operation of which K is 1, output the position of the vase in which Alice put the first flower and last one, separated by a blank. If she can not put any one, then output 'Can not put any one.'. For each operation of which K is 2, output the number of discarded flowers. 
   Output one blank line after each test case.

Sample Input

2
10 5
1 3 5
2 4 5
1 1 8
2 3 6
1 8 8
10 6
1 2 5
2 3 4
1 0 8
2 2 5
1 4 4
1 2 3

Sample Output

3 7
2
1 9
4
Can not put any one.

2 6
2
0 9
4
4 5
2 3

题意:给你N个花瓶,编号是0  到 N - 1 ,初始状态花瓶是空的,每个花瓶最多插一朵花。

然后有2个操作。

操作1,a b c ,往在a位置后面(包括a)插b朵花,输出插入的首位置和末位置。

操作2,a b ,输出区间[a , b ]范围内的花的数量,然后全部清空。

 

分析:明显的线段树,但是有一个问题就是不好寻找点,从哪一个先开始到哪一个点结束,这是一个问题,还有一个就是如何判断区间能放,是否够放的,如果能解决这个问题的话就能敲一下线段树了

怎么去解决:??? 二分枚举区间端点

像这样寻找的问题暴力不过,那就只能是二分搜索枚举了

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N= 5e4+10;
#define lson id<<1
#define rson id<<1|1
int n,m;
struct node{
    int l,r,setv;
    int sum;
//    node(int l=424,int sum=1):l(l),sum(sum) {}
}a[N<<2];
void build(int id,int l,int r){
    a[id].l=l;
    a[id].r=r;
    a[id].sum=0;
    a[id].setv=-1;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
}
void pushup(int id){
    a[id].sum=a[lson].sum+a[rson].sum;
}
void pushdown(int id){
    if(a[id].setv!=-1){
        a[lson].setv=a[rson].setv=a[id].setv;
        int len=a[id].r-a[id].l+1;
        a[lson].sum=(len-len/2)*a[id].setv;
        a[rson].sum=len/2*a[id].setv;
        a[id].setv=-1;
    }
}
void update(int id,int l,int r,int val){
    if(a[id].l==l&&a[id].r==r){
        a[id].setv=val;
        a[id].sum=(r-l+1)*val;
        return ;
    }
    pushdown(id);
    int mid=(a[id].l+a[id].r)>>1;
    if(r<=mid) update(lson,l,r,val);
    else if(l>mid) update(rson,l,r,val);
    else{
        update(lson,l,mid,val);
        update(rson,mid+1,r,val);
    }
    pushup(id);
}
int query(int id,int l,int r){
    if(a[id].l==l&&a[id].r==r)
        return a[id].sum;
    pushdown(id);
    int mid=(a[id].l+a[id].r)>>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);
}
int FindPoint(int star,int len){//len代表从当前star开始寻找len个空去放花
    int l=star,r=n;
    int ans=-1;
    while(l<=r){
        int mid=(l+r)>>1;
        int num=query(1,star,mid);//查询有多少个满的花瓶;
        if(len+num==mid-star+1)//如果当前枚举到的区间右端点到左端点的长度与需要放的空花瓶的数量+满花瓶的数量相等,那就逼近答案
            ans=mid,r=mid-1;
        else{
            if(len+num<mid-star+1)
                r=mid-1;
            else
                l=mid+1;
        }
    }
    return ans;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        build(1,1,n);
        while(m--){int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(x==1){
                y++;
                int st,ed;
                st=FindPoint(y,1);//必须有一个点放花,否则答案就是-1;
                //第二个参数入口意思时:从st开始有len个花瓶时空的
                if(st==-1) printf("Can not put any one.\n");
                else{
                    int num=query(1,st,n);//查询当前起点开始到最后一个点有多少非空花瓶;
                    int emptynum=n-st+1-num;//分空花瓶的数量;
                    if(emptynum<=z)//为什么取最小的那一个,想想
                        z=emptynum;
                    ed=FindPoint(y,z);//ed一定存在一个非负的值,因为我们已经保证空花瓶的数量z一定时存在的,那么答案肯定存在
                    printf("%d %d\n",st-1,ed-1);
                    update(1,st,ed,1);
                }
            }else
            {
                y++,z++;
                printf("%d\n",query(1,y,z));
                update(1,y,z,0);
            }
        }
        printf("\n");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值