题面
还是原题,如果不知道原题的话,题目。
现在我们要在这个最少天数下求出代价的最大值和最小值。定义代价 为每次修理的区间长度的平方。
分析
- 换汤不换药,我们先求出最小时间 T i m = ∑ i = 1 n + 1 m a x ( 0 , f [ i ] ) Tim=\sum_{i=1}^{n+1}max(0,f[i]) Tim=∑i=1n+1max(0,f[i]) ,其中 f [ i ] f[i] f[i]为差分数组
- 其实到n就行了,因为第n+1项一定是负数
- 那么我们考虑一下最大值的问题吧
- 首先我们一定是要正负一起处理的,这个是显然的吧,你要是先处理出大于0的然后打算用小于0的进行匹配,那么最大值显然是要倒序着找负值,和尽量靠前的正值匹配,这必不对啊,你这个处理得保证中间的原高度都比两头高才行哇。
- 但是呢,这样就给我们很重要的启示了:我们想尽量让区间长度大
- 考虑一下存在决策的情况是什么
- 左边有一些( ≥ 2 \geq2 ≥2)的正值,右边有至少一个负值想要与前面匹配。
- 那么我们就要用一下基本不等式啦, a 2 + b 2 2 ≥ ( a + b 2 ) 2 \frac{a^2+b^2}{2} \geq(\frac{a+b}{2})^2 2a2+b2≥(2a+b)2
- 放到题里大概就是:定长度的时候,只要两个越不接近,两者贡献就会越大
- 那么我们的贪心策略就是:尽量取靠近自己的。 (让后面的能取到尽量前面的)
- 那么最小值的策略不就也呼之欲出了嘛:尽量取远离自己的。 (让后面的能取到尽量后面的,这样长度会尽量平均)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
const int p=1e9+7;
int f[N],s[N],res[N];
int n;
long long tim;
long long ans1=0,ans2=0;
int stck[N],l=1,r=0;
inline int read()
{
int num=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {num=(num<<1)+(num<<3)+ch-'0'; ch=getchar();}
return num*f;
}
int main()
{
n=read();
for(int i=1;i<=n;i++) s[i]=read(),f[i]=s[i]-s[i-1],tim+=max(0,f[i]);
f[n+1]=-s[n];
for(int i=1;i<=n+1;i++)//最小值
{
if(f[i]>0) stck[++r]=i,res[i]=f[i];
else if(f[i]<0)
while(f[i]<0&&l<=r)
{
ans1+=1LL*(i-stck[l])*(i-stck[l])*min(-f[i],res[stck[l]])%p;
ans1%=p; int x=res[stck[l]];
res[stck[l]]-=min(-f[i],x);
f[i]+=min(-f[i],x);
if(!res[stck[l]]) l++;
}
}
printf("%lld\n",tim);
l=1;r=0;
for(int i=1;i<=n;i++) f[i]=s[i]-s[i-1]; f[n+1]-=s[n];
for(int i=1;i<=n+1;i++)//最大值
{
if(f[i]>0) stck[++r]=i,res[i]=f[i];
else if(f[i]<0)
while(f[i]<0&&l<=r)
{
ans2+=1LL*(i-stck[r])*(i-stck[r])*min(-f[i],res[stck[r]])%p;
ans2%=p;int x=res[stck[r]];
res[stck[r]]-=min(-f[i],x);
f[i]+=min(-f[i],x);
if(!res[stck[r]]) r--;
}
}
printf("%lld\n%lld\n",ans2,ans1);
return 0;
}