hdu 4614 Vases and Flowers (线段树+二分)

题目大意:

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

两种操作:

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

操作2:a b 输出区间【a,b】之间的花的数量,然后将这个区间内的花瓶清空

解题思路:

这是多校第二场的一道题目,这一道题卡到最后我们也没有做出来,比赛后搞了一下,顺便复习了一下线段树

线段树的区间更新、区间查找,时间复杂度为logN

这道题的难点在于巧妙的运用二分来寻找区间的结束位置

#include<iostream>
#include<cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int N = 50010;
const int INF = 1 << 30;
int sum[N<<2];   //记录占用区间的长度
int flag[N<<2];  //延迟标记
int len[N<<2];   //记录空闲区间的长度
int addr[N<<2];  //记录区间内第一个为空的位置
int n,q;
void Pushup(int rt,int l,int r)    //向上更新
{
    int ls = rt << 1;
    int rs = rt << 1|1;
    sum[rt] = sum[ls] + sum[rs];
    len[rt] = len[ls] + len[rs];
    addr[rt] = min(addr[ls],addr[rs]);
    return;
}
void Pushdown(int rt,int l,int r)   //向下更新延迟标记
{
    int m = (l + r) >> 1;
    int ls = rt << 1;
    int rs = rt << 1|1;
    if(flag[rt] != -1)
    {
        flag[ls] = flag[rs] = flag[rt];
        if(flag[rt] == 1)
        {
            sum[ls] = m - l + 1;
            sum[rs] = r - m;
            len[rs] = len[ls] = 0;
            addr[ls] = addr[rs] = INF;
        }
        else
        {
            sum[ls] = sum[rs] = 0;
            len[ls] = m - l + 1;
            len[rs] = r - m;
            addr[ls] = l;
            addr[rs] = m + 1;
        }
        flag[rt] = -1;
    }
    return;
}
void build(int l,int r,int rt)//建树
{
    flag[rt] = -1;
    if(l == r)
    {
        sum[rt] = 0;
        len[rt] = r - l + 1;
        addr[rt] = l;
        return;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    Pushup(rt,l,r);
    return;
}
int QuerySum(int L,int R,int l,int r,int rt) //统计区间内非空的位置数
{
    if(L <= l && r <= R)    return sum[rt];
    Pushdown(rt,l,r);
    int m = (l + r) >> 1;
    int res = 0;
    if(L <= m)  res += QuerySum(L,R,lson);
    if(R > m)   res += QuerySum(L,R,rson);
    Pushup(rt,l,r);
    return res;
}
int QueryPos(int L,int R,int l,int r,int rt)//查找区间内第一个非空的位置
{
    if(L <= l && r <= R)
    {
        return addr[rt];
    }
    Pushdown(rt,l,r);
    int m = (l + r) >> 1;
    int tmp = INF;
    if(L <= m)  tmp = min(tmp,QueryPos(L,R,lson));
    if(R > m)   tmp = min(tmp,QueryPos(L,R,rson));
    Pushup(rt,l,r);
    return tmp;
}
void Update(int L, int R, int c, int l, int r, int rt)  //更新区间
{
    if(L <= l && r <= R)
    {
        flag[rt] = c;
        if(flag[rt] == 1)   //延迟标记
        {
            sum[rt] = r - l + 1;
            len[rt] = 0;
            addr[rt] = INF;
        }
        else
        {
            sum[rt] = 0;
            len[rt] = r - l + 1;
            addr[rt] = l;
        }
        return;
    }
    Pushdown(rt,l,r);
    int m = (l + r) >> 1;
    if(L <= m)  Update(L,R,c,lson);
    if(R > m)   Update(L,R,c, rson);
    Pushup(rt,l,r);
    return;
}
int binary_search(int l, int r, int st, int tar)//二分查找
{
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        int sum = QuerySum(st, mid, 0, n - 1, 1);
        sum = mid - st +1 -sum;
        if(sum >= tar)  r = mid;
        else    l = mid + 1;
        if(l == r)  return l;
    }
    return l;
}
int main()
{
    //freopen("Input.txt","r",stdin);
    int T;
    int k,a,b,i;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        build(0,n-1,1);  //构建线段树
        for(i = 0; i < q; i++)
        {
            scanf("%d%d%d",&k,&a,&b);
            if(k == 1)   //第一种操作
            {
                if(b == 0)  continue;
                int ans = QuerySum(a, n-1, 0, n-1, 1);  //查询从a到末尾的区间内有多少非空的位置
                if(ans == n-a)  //不存在非空的位置
                    puts("Can not put any one.");
                else
                {
                    int p = min(n - a - ans, b); //可以插的花的数量
                    int left = QueryPos(a, n-1, 0, n-1, 1); //查找区间内第一个空位
                    int right = binary_search(a, n-1, a, p);//二分查找末尾的位置
                    printf("%d %d\n",left,right);
                    Update(left, right, 1, 0, n-1, 1); //更新区间内的值
                }
            }
            else
            {
                int ans = QuerySum(a, b, 0, n-1, 1);
                printf("%d\n",ans);
                Update(a, b, 0, 0, n-1, 1);   //更新区间内的值
            }
        }
        puts("");
    }
    return 0;
}



你可能感兴趣的:(hdu 4614 Vases and Flowers (线段树+二分))