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;
}