铺设道路(新)

4 篇文章 0 订阅
1 篇文章 0 订阅

题面

还是原题,如果不知道原题的话,题目
现在我们要在这个最少天数下求出代价的最大值最小值。定义代价 为每次修理的区间长度的平方

分析

  • 换汤不换药,我们先求出最小时间 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值