BZOJ4239(线段树)

一道权限题

题面

描述
农夫Byteasar买了一片n亩的土地,他要在这上面种草。 他在每一亩土地上都种植了一种独一无二的草,其中,第i亩土地的草每天会长高a[i]厘米。 Byteasar一共会进行m次收割,其中第i次收割在第d[i]天,并把所有高度大于等于b[i]的部分全部割去。Byteasar想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?
Input
第一行包含两个正整数n,m(1<=n,m<=500000),分别表示亩数和收割次数。 第二行包含n个正整数,其中第i个数为ai,依次表示每亩种植的草的生长能力。 接下来m行,每行包含两个正整数d[i],bi,依次描述每次收割。 数据保证d[1] < d[2] <… < d[m],并且任何时刻没有任何一亩草的高度超过10^12。
Output
输出m行,每行一个整数,依次回答每次收割能得到的草的高度总和。
样例输入
4 4
1 2 4 3
1 1
2 2
3 0
4 4
样例输出
6
6
18
0


思想是比较奇妙的。

仔细读题,我们发现草的高度成一个单调性(因为每株草每天的生长量是一定的),考虑把草sort排序,那么每次先找到第一个大于b的草,从它开始后面的草都要被割

维护这样一些信息:

day:从上一次收割到这一次过去的天数

now:当前草的高度(每一次割草后那一串的草的高度都是b,那么对应区间now为b)

ans:区间草高度和

maxx:区间最高草的高度(对于每一次操作,如果最高草的高度都小于等于b,就可以不操作了)

标记下传时,先传now,再传day(类比于赋值标记和加标记)。

#include<bits/stdc++.h>
#define LL long long
#define N 500003
using namespace std;
LL read()
{
    LL x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
LL a[N],sum[N];
LL lc[N<<2],rc[N<<2],ans[N<<2],maxx[N<<2];
LL day[N<<2],now[N<<2];
LL ndnum=0;
void pushup(LL k)
{
    maxx[k]=max(maxx[lc[k]],maxx[rc[k]]);
    ans[k]=ans[lc[k]]+ans[rc[k]];
}
void build(LL k,LL l,LL r)
{
    if(l==r){now[k]=-1;return;}
    LL mid=(l+r)>>1;
    build(lc[k]=++ndnum,l,mid);
    build(rc[k]=++ndnum,mid+1,r);
    pushup(k);
}
void push1(LL k,LL l,LL r,LL v)//now的下传 
{
    now[k]=v;
    maxx[k]=v;
    ans[k]=(r-l+1)*v;//相当于直接覆盖 
    day[k]=0;//当前值变了,存的天数也就没什么用了 
}
void push2(LL k,LL l,LL r,LL v)//day的下传 
{
    day[k]+=v;
    ans[k]+=(sum[r]-sum[l-1])*v;
    maxx[k]+=v*a[r]; 
}
void pushdown(LL k,LL l,LL r)
{
    LL mid=(l+r)>>1;
    if(now[k]!=-1)
    {
        push1(lc[k],l,mid,now[k]);
        push1(rc[k],mid+1,r,now[k]);
        now[k]=-1;
    }
    if(day[k])
    {
        push2(lc[k],l,mid,day[k]);
        push2(rc[k],mid+1,r,day[k]);
        day[k]=0;
    }
}
void modify(LL k,LL L,LL R,LL l,LL r,LL v)
{
    if(L>=l&&R<=r)
    {
        push1(k,L,R,v);
        return;
    }
    LL mid=(L+R)>>1;
    pushdown(k,L,R);
    if(l<=mid)modify(lc[k],L,mid,l,r,v);
    if(r>mid)modify(rc[k],mid+1,R,l,r,v);
    pushup(k);
}
LL query(LL k,LL l,LL r,LL v)
{
    if(l==r)return l;
    LL mid=(l+r)>>1;
    pushdown(k,l,r);
    if(maxx[lc[k]]<=v)return query(rc[k],mid+1,r,v);
    else return query(lc[k],l,mid,v);
}
int main()
{
    LL n=read(),m=read();
    build(1,1,n);
    for(int i=1;i<=n;++i)a[i]=read();
    sort(a+1,a+1+n);
    for(int i=1;i<=n;++i)
      sum[i]=a[i]+sum[i-1];
    LL d1=0;LL d2; 
    while(m--)
    {
        d2=read();LL b=read();
        day[1]+=d2-d1;maxx[1]+=(d2-d1)*a[n];ans[1]+=sum[n]*(d2-d1);
        d1=d2;
        if(maxx[1]<=b)
        {
            printf("0\n");
            continue;
        }
        LL res=ans[1];
        LL pos=query(1,1,n,b);
        modify(1,1,n,pos,n,b);
        printf("%lld\n",res-ans[1]);
    }
} 
View Code

 

转载于:https://www.cnblogs.com/yyys-/p/11324469.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值