【解题报告】HDU - 4614 Vases and Flowers (线段树+二分思想)

原题地址

题意

整个题目抽象下来就是说给一个长度为n的初始全为0的串,然后接下来有m个操作,操作1是从第a个位置开始,一直到n个位置,将b个0变成1,如果从a开始没有0存在输出“Can not put any one.",否则就进行变更(变更的个数可以小于b)并且输出变更的区间。操作2是问a到b位置之间有多少个1,并且将a到b位置全部变为0。

思路

这个题目和上次做过的那个摧毁村庄的题目很像,当时那个题目是问连续的村庄有多少个,那样要用线段树维护左边连续和右边连续的最大值,或者用树状数组直接二分就可以解决,这个题目的限制要小一些,反而更加难处理,常规线段树是可以维护一个区间和的(要用lazy标记防止超时),那么我们想怎么样就使用常规线段树来解决这个问题。首先线段树的查询操作需要一个区间,这正好对应第二种操作,比较简单。而第一种操作,我们想到,线段树的区间修改也需要一个区间,那么问题就变成了我们要在(a, n)这个区间上找到一个区间(l, r),使得(l, r)的区间和正好等于所要变更的b和(a, n)上所剩下的0的最小值,因此我们想到了二分,我们先在(a , n)上找到第一个满足位置是0的l,然后再在(a, n)上找到第一个满足(a, r)上有b个0的r,这样我们就找到了我们要修改的区间,输出这个区间(注意原题是按照0~n-1编号的)并修改这个区间上的串。

#include 
#include 
using namespace std;
const static int maxn = 5e4+10;
int n, m;

struct segmentree
{
    int l, r;
    int lazy;
    int sum;
}st[maxn*4];
void pushup(int rt)
{
    st[rt].sum = st[rt<<1].sum + st[rt<<1|1].sum;
}
void pushdown(int rt)
{
    if(~st[rt].lazy)
    {
        st[rt<<1].lazy = st[rt<<1|1].lazy = st[rt].lazy;
        int mid = (st[rt].l + st[rt].r) >> 1;
        st[rt<<1].sum = (mid - st[rt].l + 1) * st[rt<<1].lazy;
        st[rt<<1|1].sum = (st[rt].r - mid) * st[rt<<1|1].lazy;
        st[rt].lazy = -1;
    }
}

void buildtree(int l, int r, int rt)
{
    st[rt].l = l;
    st[rt].r = r;
    st[rt].lazy = -1;
    if(l == r)
    {
        st[rt].sum = 1;
        return;
    }
    int mid = (l + r) >> 1;
    buildtree(l, mid, rt<<1);
    buildtree(mid+1, r, rt<<1|1);
    pushup(rt);
}

int query(int L, int R, int rt)
{
    if(st[rt].l >= L && st[rt].r <= R)
    {
        return st[rt].sum;
    }
    pushdown(rt);
    int mid = (st[rt].l + st[rt].r) >> 1;
    int ans = 0;
    if(mid >= L)
        ans += query(L, R, rt<<1);
    if(mid < R)
        ans += query(L, R, rt<<1|1);
    return ans;
}

void update(int L, int R, int rt, int c)
{
    if(st[rt].l >= L && st[rt].r <= R)
    {
        st[rt].sum = (st[rt].r - st[rt].l + 1) * c;
        st[rt].lazy = c;
        return;
    }
    pushdown(rt);
    int mid = (st[rt].l + st[rt].r) >> 1;
    if(mid >= L)
        update(L, R, rt<<1, c);
    if(mid < R)
        update(L, R, rt<<1|1, c);
    pushup(rt);
}

int findleft(int l)
{
    int r = n;
    while(l < r)
    {
        int mid = (r + l)>>1;
        if(query(l, mid, 1) >= 1)
            r = mid;
        else
            l = mid+1;
    }
    return r;
}
int findright(int t, int num)
{
    int l = t;
    int r = n;
    while(l < r)
    {
        int mid = (l + r)>>1;
        if(query(t, mid, 1) >= num)
            r = mid;
        else
            l = mid+1;
    }
    return r;
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d %d", &n, &m);
        buildtree(1, n, 1);
        int op, a, b;
        while(m--)
        {
            scanf("%d %d %d", &op, &a, &b);
            if(op == 1)
            {
                a++;
                int left = query(a, n, 1);
                //printf("%d\n", left);
                if(left == 0)
                {
                    printf("Can not put any one.\n");
                    continue;
                }
                int l = findleft(a);
                int r = findright(a, min(left, b));
                printf("%d %d\n", l-1, r-1);
                update(l, r, 1, 0);
            }
            else
            {
                a++;
                b++;
                printf("%d\n", (b-a+1) - query(a, b, 1));
                update(a, b, 1, 1);
            }
        }
        printf("\n");
    }
    return 0;
}

你可能感兴趣的:(题解)