hdu4614 : Vases and Flowers

文章介绍了一种利用树状数组(也称作线段树)来高效处理区间内空花瓶数量的动态更新和查询问题。通过二分查找确定插入花瓶的位置,并维护一个sum数组和flag数组来跟踪区间状态。代码示例展示了如何构建、更新和查询树状数组。
摘要由CSDN通过智能技术生成

题目链接如下所示:

Problem - 4614

思路是这样,维护一个sum数组,记录区间内的空花瓶的数目,维护一个flag数组,给区间打上懒标记,如果是1,则代表是空花瓶标记,0则是有花瓶标记,-1表示对这个区间不做任何操作。

如果要插花,我们需要定位有f个空花瓶的区间,这可以用二分来找,即找从a开始的有f个空花瓶的区间,定位左区间用l=bin(a,1),定位右区间就是r=bin(l,f),注意这里的f如果比剩下到N-1总共的空花瓶要少时,需要把f换成num。

代码如下所示:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<vector>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<cmath>
#include<climits>

using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int MAXN=50005;
int sum[MAXN<<2],flag[MAXN<<2];
int T,M,N;

void push_up(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void push_down(int rt,int l,int r){
    if (flag[rt]==-1) return;
    flag[rt<<1]=flag[rt<<1|1]=flag[rt];
    int mid=(l+r)>>1;
    sum[rt<<1]=(mid-l+1)*flag[rt];
    sum[rt<<1|1]=(r-mid)*flag[rt];
    flag[rt]=-1;
}

void build(int l,int r,int rt){
    if (l==r){
        sum[rt]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    push_up(rt);
}

void update(int ul,int ur,int val,int l,int r,int rt){
    if (ul<=l && r<=ur){
        sum[rt]=val*(r-l+1);
        flag[rt]=val;
        return;
    }
    push_down(rt,l,r);
    int mid=(l+r)>>1;
    if (ul<=mid){
        update(ul,ur,val,lson);
    }
    if (ur>mid){
        update(ul,ur,val,rson);
    }
    push_up(rt);
}

int query(int ul,int ur,int l,int r,int rt){
    if (ul<=l && r<=ur){
        return sum[rt];
    }
    push_down(rt,l,r);
    int ans=0;
    int mid=(l+r)>>1;
    if (ul<=mid){
        ans+=query(ul,ur,lson);
    }
    if (ur>mid){
        ans+=query(ul,ur,rson);
    }
    return ans;
}

int bin_search(int start,int sum){
    int l=start, r=N;
    while (l<=r){
        int mid=(l+r)>>1;
        int num= query(start,mid,1,N,1);
        if (num>sum || (num==sum && query(mid,mid,1,N,1)==0)){
            r=mid;
        }else if (num<sum){
            l=mid+1;
        } else{
            return mid;
        }
    }
    return -1;
}

int main(){
    int k,a,f,l,r;
    scanf("%d",&T);
    while (T--){
        scanf("%d %d",&N,&M);
        memset(flag,-1,sizeof(flag));
//        cout<<flag[0]<<endl;
        build(1,N,1);
//        cout<<"T:"<<T<<",N:"<<N<<",M:"<<M<<endl;
        while (M--){
            scanf("%d",&k);
//            cout<<"k:"<<k<<endl;
            if (k==1){
                scanf("%d %d",&a,&f);
//                cout<<"k:"<<k<<",a:"<<a<<",f:"<<f<<endl;
                a++;
                int num= query(a,N,1,N,1);
//                cout<<"num:"<<num<<endl;
                if (num==0){
                    printf("Can not put any one.\n");
                } else{
                    int spos= bin_search(a,1);
                    if (num<f) f=num;
                    int epos= bin_search(spos,f);
//                    cout<<"spos:"<<spos<<",epos:"<<epos<<endl;
                    update(spos,epos,0,1,N,1);
                    printf("%d %d\n",spos-1,epos-1);
                }
            } else{
                scanf("%d %d",&l,&r);
//                cout<<"k:"<<k<<",l:"<<l<<",r:"<<r<<endl;
                l++,r++;
                int ans=(r-l+1)- query(l,r,1,N,1);
                printf("%d\n",ans);
                update(l,r,1,1,N,1);
            }
        }
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值