BZOJ4293 Siano

题头:

描述
农夫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

一道比较那啥的线段树题
仔细读题
我们需要两个标记
一个mark进行区间加的操作(mark维护天数,实际增加的值是mark[ i ]*a[ i ])
还需要一个marks进行区间更改为的操作
然后对于每次输入
mark增加的值为这一次输入的时间减上一次输入的时间
然后我们维护两个值:区间和和区间最大值(两次区间和相减即使所求答案,区间最大值是方便进行区间更改为的操作,因为这样我们只需要下传标记到区间最大值大于b[ i ]的区间)
维护区间和需要把区间a的值的和乘上mark,区间最大值加上天数乘区间a的最大值;
我们只需要先从小到大排个序,保证单调性,这样找到最后一个高度小于b[ i ]的位置,我们就可以直接把从这儿往后都打上标记

代码如下

//siano
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline ll read(){
    char ch;
    while((ch=getchar())<'0'||ch>'9'){;}
    ll res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
    res=res*10+ch-'0';
    return res;
} 
long long a[500005],tre[2000007],mark[2000007],maxn[2000007],sum[500005],marks[2000007],d1;
//tre:记录区间和,sum
int n,m,l[2000007],r[2000007];
inline ll max(ll a,ll b)
{
    return a>b?a:b;
}
inline void buildtree(int p,int st,int end){
    l[p]=st,r[p]=end,mark[p]=0,marks[p]=-1;
//marks初始化为-1,不能初始化为0(有可能是更改为0)
    if(st==end){tre[p]=maxn[p]=0;return;}
    int mid=(st+end)>>1;
    buildtree(p<<1,st,mid),buildtree((p<<1)+1,mid+1,end);
}
inline void pushup(int root)
//向上传递
{
    maxn[root]=max(maxn[(root<<1)],maxn[(root<<1)+1]);
    tre[root]=tre[(root<<1)]+tre[(root<<1)+1];
}
inline void pushnow(int root,ll v){
//用mark更改区间
    mark[root]+=v;
    tre[root]+=v*(sum[r[root]]-sum[l[root]-1]);
    maxn[root]+=v*a[r[root]];
}
inline void pushnown(int root,ll k)
//用marks更改区间
{
    marks[root]=k;
    maxn[root]=k;
    tre[root]=k*(r[root]-l[root]+1);
    mark[root]=0;
//因为更改区间以后前面的操作打下的mark,对当前答案不会有影响,所以把mark变为0;
}
inline void pushDown(int root)
//向下传递
{
    if(marks[root]!=-1)
    {
        pushnown((root<<1),marks[root]);
        pushnown((root<<1)+1,marks[root]);
        marks[root]=-1;
    }
    if(mark[root])
    {
        pushnow((root<<1),mark[root]);
        pushnow((root<<1)+1,mark[root]);
        mark[root]=0;
    }
}
inline void modify(int root,int ql,int qr,ll v){
//更改区间值
    if(ql>r[root]||qr<l[root])return;
    if(ql<=l[root]&&r[root]<=qr){pushnown(root,v);return;}
    pushDown(root);
    int mid=(l[root]+r[root])>>1;
    if(qr<=mid)modify((root<<1),ql,qr,v);
    else if(ql>mid)modify((root<<1)+1,ql,qr,v);
    else modify((root<<1),ql,mid,v),modify((root<<1)+1,mid+1,qr,v);
    pushup(root);
}
inline ll query(int root,ll v){
//查找第一个小于等于b[ i ]的位置    if(l[root]==r[root])return l[root];
    pushDown(root);
    if(maxn[(root<<1)]<=v)return query((root<<1)+1,v);
    return query((root<<1),v);
}

int  main(){
    n=read(),m=read();
    for(int  i=1;i<=n;i++)
    {
        a[i]=read();
    }
    sort(a+1,a+1+n);
    buildtree(1,1,n);
    for(int i=1;i<=n;i++)
    sum[i]=sum[i-1]+a[i];
    d1=0;
    for(int i=1;i<=m;i++)
    {
        ll d2=read(),b=read();
        maxn[1]+=(d2-d1)*a[n],tre[1]+=(d2-d1)*sum[n],mark[1]+=(d2-d1);
        if(maxn[1]<=b)
        {
            puts("0"),d1=d2;
            continue;
        }
        ll res=tre[1];
        int  pos=query(1,b);
        modify(1,pos,n,b);
        cout<<res-tre[1]<<'\n',d1=d2;
    }
    return 0;
}
posted @ 2018-08-06 10:12  Stargazer_cyk  阅读(113)  评论(0编辑  收藏  举报