题解 P5579 【[PA2015]Siano】
这里有一个O(n) 的做法
首先先来考虑下面一种情况,假设第
好了基本思想有了,但是注意一个问题,并不是所有的需要切割的草在上一次切割时都恰好为所以上边讨论了五百多字就白扯了吗,显然不是,还是有一部分草是满足上述的切割办法的,也就是两次都被切割的,所以只需要特殊考虑上一次没有被切割但是下一次它长到了可以被切割的高度的草,把这些草处理了就行。通过上一次的切割处理这个很不好处理,但是我们可以发现,如果我们定义第0天时割掉了所有高度大于0的草(对答案没有影响因为初始时都是0),那么假设当前时刻为
注意到草的生长速度是有单调性的,在不去割草的情况下生长速度越大的高度一定越大废话,于是可以维护一个
但是这样会不会漏掉什么情况呢?答案是不会,因为每一次的割草都是割的连续的速度,只可能出现重复情况,而重复情况上边已经排除,所以这种做法是可以的。
由于本人蒟蒻,可能有些地方写的不严谨或者不清楚,又或者有的地方写错了。。。反正感觉不对的地方欢迎指出来。
借鉴于https://www.cnblogs.com/wangyurzee7/p/5224273.html
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int lqs=5e5+10,m=1e6;
int day,a[lqs<<1],n,stk[lqs],top;
ll s[lqs<<1],d[lqs],b[lqs],to[lqs];
int main(){
scanf("%d%d",&n,&day);
for(int w,i=1;i<=n;i++)
scanf("%d",&w),a[w]++;
for(int i=1;i<=day;i++)
scanf("%lld%lld",&d[i],&b[i]);
for(int i=1;i<=m;i++)
s[i]=s[i-1]+1ll*a[i]*i;
for(int i=1;i<=m;i++)
a[i]+=a[i-1];
top=1;
for(int i=1;i<=day;i++){
ll res=0;
int last=m,x=max(to[stk[top]],min((b[i]-b[stk[top]])/(d[i]-d[stk[top]]),1ll*m));
while(x<last){
res+=(d[i]-d[stk[top]])*(s[last]-s[x])+(b[stk[top]]-b[i])*(a[last]-a[x]);
last=x;
if(last>to[stk[top]])break;
if(top==0)break;top--;
x=max(to[stk[top]],min((b[i]-b[stk[top]])/(d[i]-d[stk[top]]),1ll*m));
}
to[i]=last;
if(to[i]<m)stk[++top]=i;
printf("%lld\n",res);
}
}