[海军国际项目办公室]铺设道路

铺设道路

题目

在这里插入图片描述
在这里插入图片描述

题解

首先,我们有一个对于区间加减的很经典的转化,通过差分的转化使得我们的区间加减变成单点的改变。
我们定义 b i = d i − d i − 1 b_i=d_{i}-d_{i-1} bi=didi1,那么我们对区间 ( l , r ) (l,r) (l,r)进行操作相当于让 b l − 1 b_{l}-1 bl1, b r + 1 + 1 b_{r+1}+1 br+1+1,其代价为 ( r − l + 1 ) 2 (r-l+1)^2 (rl+1)2
显然, d i = ∑ j = 1 i b j d_{i}=\sum_{j=1}^{i}b_{j} di=j=1ibj,也就是说在合法情况下,我们的 d d d的前缀和应该是始终不小于 0 0 0的。
由于要让总的操作次数最小,我们肯定只会让 b > 0 b>0 b>0 b l − 1 b_{l}-1 bl1,让 b r + 1 < 0 b_{r+1}<0 br+1<0 b r + 1 + 1 b_{r+1}+1 br+1+1
显然,既然它的前缀和是始终大于 0 0 0的,所以我们是一定找得到这样的匹配的 ( l , r ) (l,r) (l,r)
而我们最后的到的序列有是一个全 0 0 0的序列,而我们有只会让 d d d减小,所以我们在过程中一定是保证 d d d全部大于 0 0 0,即所有操作都是合法的。

最小操作次数显然是 ∑ max ⁡ ( b i , 0 ) \sum \max(b_{i},0) max(bi,0)
我们关键是要怎么匹配 b b b使得我们最后的代价和最大于最小。
由于我们的代价是平方项,而它们一次方状况下的总和是固定的,所以我们肯定要让它们长的段越多权值肯定越大,较长的段越小权值肯定越小。
所以我们肯定是对于 b i < 0 b_{i}<0 bi<0 b i b_{i} bi匹配离他最近的 b j > 0 b_{j}>0 bj>0 b j b_{j} bj,将更远的留给后面使得我们的总贡献越大。
而将其匹配最远的 b j < 0 b_{j}<0 bj<0 b j b_{j} bj可以使得所有的长度都较短,使得总贡献较小。
该匹配过程显然可以通过队列进行维护。

时间复杂度 O ( n ) O\left(n\right) O(n)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x<<'\n'
typedef long long LL;
typedef unsigned long long uLL;     
const LL INF=0x3f3f3f3f3f3f3f3f;  
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int n1=50;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
char gc(){static char buf[10000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,10000000,stdin),p1==p2)?EOF:*p1++;}
//#define getchar gc
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,d[MAXN],b[MAXN],ansmax,ansmin;LL ans;
deque<pii>q;
signed main(){
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	read(n);for(int i=1;i<=n;i++)read(d[i]);
	for(int i=1;i<=n+1;i++)b[i]=d[i]-d[i-1],ans+=1ll*max(b[i],0);
	for(int i=1;i<=n+1;i++){
		if(b[i]>0)q.push_back(mkpr(i,b[i]));
		else while(b[i]<0){
			pii t=q.front();q.pop_front();
			int tmp=min(-b[i],t.sec);b[i]+=tmp;t.sec-=tmp;
			Add(ansmin,1ll*(i-t.fir)*(i-t.fir)%mo*tmp%mo,mo);
			if(t.sec)q.push_front(t);
		}
	}
	while(!q.empty())q.pop_back();
	for(int i=1;i<=n+1;i++)b[i]=d[i]-d[i-1];
	for(int i=1;i<=n+1;i++){
		if(b[i]>0)q.push_back(mkpr(i,b[i]));
		else while(b[i]<0){
			pii t=q.back();q.pop_back();
			int tmp=min(-b[i],t.sec);b[i]+=tmp;t.sec-=tmp;
			Add(ansmax,1ll*(i-t.fir)*(i-t.fir)%mo*tmp%mo,mo);
			if(t.sec)q.push_back(t);
		}
	}
	printf("%lld\n%d\n%d\n",ans,ansmax,ansmin);
	return 0;
}

谢谢!!!

posted @ 2021-10-26 17:05  StaroForgin  阅读(7)  评论(0)    收藏  举报  来源