2013 多校第二场 hdu 4614 Vases and Flowers(线段树)

hdu 4614
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4614
题目大意:n个花瓶,m个操作,k=1 时是从 a 开始插花,如果已经有花,那么跳过,能插多少插多少。k=2,把区间[ a , b ] 内的有的花都清理掉。
思路:裸线段树。就是找区间的时候比较麻烦,其实写两个二分先判断出左右边界就好了。。 时间是 2s ,再乘个 logn 应该也没问题的。。比赛的时候一直卡题,导致后面看到这道裸线段树都懒得去想,懒得去敲了。。 囧

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 55555 ;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

int num[MAXN<<2],dly[MAXN<<2];

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

void push_down(int rt,int l,int r,int m)
{
    num[rt<<1] = dly[rt]*(m-l+1);
    num[rt<<1|1] = dly[rt]*(r-m);
    dly[rt<<1] = dly[rt<<1|1] =dly[rt];
    dly[rt] = -1;
}

void build(int l,int r,int rt)
{
    dly[rt] = -1;
    if(l==r)
    {
        num[rt] = 1 ;
        return ;
    }
    int m = l+r>>1;
    build(lson);
    build(rson);
    push_up(rt);
}

int query(int l,int r,int rt,int a,int b)
{
   if(a<=l&&b>=r)
   {
       return num[rt];
   }
   int m = l+r>>1;
   if(dly[rt]!=-1) push_down(rt,l,r,m);
   int cnt1 = 0,cnt2 = 0;
   if(a<=m)
    cnt1 = query(lson,a,b);
   if(b>m) cnt2 = query(rson,a,b);
   return cnt1+cnt2;

}

void update(int l,int r,int rt,int a,int b,int c)
{

    if(a<=l&&b>=r)
    {
        dly[rt]  = c;
        num[rt] = c*(r-l+1);
        //printf("l = %d,r = %d,rt = %d,a = %d,b = %d,c = %d,num = %d\n",l,r,rt,a,b,c,num[rt]);
        return ;
    }
    int m = l+r>>1;
    if(dly[rt]!=-1)
    {
        push_down(rt,l,r,m);
    }
    if(a<=m)
        update(lson,a,b,c);
    if(b>m) update(rson,a,b,c);
    push_up(rt);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        build(0,n-1,1);
        int flag,a,b;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&flag,&a,&b);
            if(flag==1)
            {
                int s = query(0,n-1,1,a,n-1);
                if(s==0) puts("Can not put any one.");
                else
                {
                    int ll,rr;
                    if(s<=b)
                    {
                        b=s;
                    }
                    int l = a,r = n-1;
                    while(l<r)
                    {
                        int m = l+r>>1;
                        int tmp = query(0,n-1,1,a,m);
                        //printf("l = %d,r = %d,m = %d,tmp = %d\n",l,r,m,tmp);
                        if(tmp==b)
                            r = m;
                        else if(tmp>b)
                            r = m-1;
                        else l = m+1;
                    }
                    rr = l;
                    
                    l = a,r = rr;
                    while(l<r)
                    {
                        int m = (l+r+1)>>1;
                        int tmp = query(0,n-1,1,m,rr);
                        //printf("l = %d,r = %d,m = %d,tmp = %d\n",l,r,m,tmp);
                        if(tmp==b)
                            l = m;
                        else if(tmp>b)
                            l = m+1;
                        else r = m-1;
                    }
                    ll = l;
                    printf("%d %d\n",ll,rr);
                    update(0,n-1,1,ll,rr,0);
                }
            }
            else
            {
                printf("%d\n",b-a+1-query(0,n-1,1,a,b));
                update(0,n-1,1,a,b,1);
            }
        }
        puts("");
    }
    return 0;
}


上面那个是用二分的,复杂度上多了个 logn,后来问了下其他人,这道题也可以不用二分,直接用纯线段树的方法做,本来以为找位子会很麻烦,可是经过他们点拨之后,其实也感觉挺简单的,只能说自己的线段树功力还不够吧。就是先把0到一开始尝试放的位子之前有几个空花瓶算出来,记为s,然后找和为s+1的区间,这就是l,然后再找和为s+min(tot,c)(tot为一开始尝试放的位子到n-1的总共空花瓶数,c为放花数),这就是r,然后再更新就好了。个人感觉,把开始放花的边界拓展为0是关键。

具体过程看代码吧,代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define lson  l,m,rt<<1
#define rson  m+1,r,rt<<1|1

const int MAXN =  50001 ;

int cnt[MAXN<<2],dly[MAXN<<2];

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

void build(int l,int r,int rt)
{
    dly[rt] = -1;
    if(l==r)
    {
        cnt[rt] = 1;
        return ;
    }
    int m = l+r>>1;
    build(lson);
    build(rson);
    push_up(rt);
}

void push_down(int rt,int l,int r,int m)
{
    cnt[rt<<1] = dly[rt]*(m-l+1);
    cnt[rt<<1|1] = dly[rt]*(r-m);
    dly[rt<<1] = dly[rt<<1|1] =dly[rt];
    dly[rt] = -1;
}

void update(int l,int r,int rt,int a,int b,int c)
{
    if(a<=l&&b>=r)
    {
        dly[rt] = c;
        cnt[rt] = c*(r-l+1);
        return ;
    }
    int m = l+r>>1;
    if(dly[rt]!=-1) push_down(rt,l,r,m);
    if(a<=m) update(lson,a,b,c);
    if(b>m) update(rson,a,b,c);
    push_up(rt);
}

int query1(int l,int r,int rt,int a,int b)
{
    if(a<=l&&b>=r)
    {
        return cnt[rt];
    }
    int m = l+r>>1;
    if(dly[rt]!=-1) push_down(rt,l,r,m);
    int cnt1 = 0,cnt2 = 0;
    if(a<=m)
        cnt1 = query1(lson,a,b);
    if(b>m)
        cnt2 = query1(rson,a,b);
    return cnt1+cnt2;
}

int query2(int l,int r,int rt,int c)
{
    if(l==r)
        return l;
    int m = l+r>>1;
    if(dly[rt]!=-1) push_down(rt,l,r,m);
    if(cnt[rt<<1]<c)
    {
        return query2(rson,c-cnt[rt<<1]);
    }
    else return query2(lson,c);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        build(0,n-1,1);
        int a,b,c;
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(a==1)
            {
                int tot = query1(0,n-1,1,b,n-1);
                if(tot==0)
                {
                    puts("Can not put any one.");
                    continue;
                }
                c = min(c,tot);
                int s;
                if(b==0)
                    s=0;
                else s = query1(0,n-1,1,0,b-1);
                int l = query2(0,n-1,1,s+1);
                int r = query2(0,n-1,1,s+c);
                printf("%d %d\n",l,r);
                update(0,n-1,1,l,r,0);
            }
            else
            {
                printf("%d\n",c-b+1-query1(0,n-1,1,b,c));
                update(0,n-1,1,b,c,1);
            }
        }
        puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值