【模拟赛】数列题(斜率优化,李超树)

背景

灵感源于知乎

有一天,OneInDark 大神正端坐在龙椅上,品尝着一小勺中子星。

刚吃下去的太阳还热乎,桌上摆着大大小小的碗碟,盛着有一打黑洞、一块巴掌大的反物质、一罐紧密贴合的纯质子……旁边还放着一个直径一光年的地球供祂享用。

这时,一个大臣无初速度、恒定加速度加速数年至光速的 99.9999 ⋯ 99.9999\cdots 99.9999 终于到达 OneInDark 面前,对他说:

“皇上,奇变偶不变?”

OneInDark 一听,这是配得上祂的问题?立马生气了,放了一个红色的屁,用祂的特殊超能力——无法毁灭的一根手指 猛地砸了一下地面,顿时皇家游泳池连同里面漂浮着的一个人一起被相对静止地击飞,并从2000米的高空忽视空气摩擦自由落体坠入大海……

“给我来道数列题解气!”……

题面

对于数列 A A A ,你需要选择若干个下标 1 ≤ p 1 < p 2 < . . . < p m − 1 < n 1\leq p_1<p_2<...<p_{m-1}<n 1p1<p2<...<pm1<n

B 1 B_1 B1 为数列 A A A 中区间 [ 1 , p 1 ] [1,p_1] [1,p1] 的最大值,对于 j ∈ [ 2 , m − 1 ] j\in[2,m-1] j[2,m1] B j B_j Bj 为数列 A A A 中区间 ( p j − 1 , p j ] (p_{j-1},p_j] (pj1,pj] 的最大值, B m B_m Bm 为数列 A A A 中区间 ( p m − 1 , n ] (p_{m-1},n] (pm1,n] 的最大值。

你需要最大化:
∑ i = 2 m ( ( B i − B i − 1 ) 2 + C ) \sum_{i=2}^m \Big( (B_i-B_{i-1})^2+C \Big) i=2m((BiBi1)2+C)

特别的,如果你划分出的段数仅有 1 1 1 段,则答案为 0 0 0

2 ≤ n ≤ 1 0 6 , 1 ≤ A i ≤ 1 0 6 , 0 ≤ C ≤ 1 0 10 2\leq n\leq10^6,1\leq A_i\leq10^6,0\leq C\leq10^{10} 2n106,1Ai106,0C1010

题解

不难把题面转换一下:

对于数列 A A A ,你需要选择若干个下标 1 ≤ p 1 < p 2 < . . . < p m ≤ n 1\leq p_1<p_2<...<p_{m}\leq n 1p1<p2<...<pmn ,令 p 0 = 0 , p m + 1 = n + 1 p_0=0,p_{m+1}=n+1 p0=0,pm+1=n+1 ,满足
∀ i ∈ [ 0 , m ] , ( max ⁡ p i < j < p i + 1 A j ) ≤ max ⁡ ( A p i , A p i + 1 ) \forall i\in [0,m],\left(\max_{p_{i}<j<p_{i+1}}A_j\right) \leq \max(A_{p_i},A_{p_{i+1}}) i[0,m],(pi<j<pi+1maxAj)max(Api,Api+1)

最大化:
∑ i = 2 m ( ( A p i − A p i − 1 ) 2 + C ) \sum_{i=2}^m \Big( (A_{p_i}-A_{p_{i-1}})^2+C \Big) i=2m((ApiApi1)2+C)

我们发现上面的条件对于下面的最大值貌似没有甚么限制,

事实上,可以证明,若无视上面的条件,直接求下面的最大值,最优方案一定是符合上面的条件的:

若存在 i < j < k i<j<k i<j<k A j A_j Aj 最大,不妨设 A i < A k < A j A_i<A_k<A_j Ai<Ak<Aj,则连续选 A i , A k A_i,A_k Ai,Ak 的贡献是 ( A k − A i ) 2 + C (A_k-A_i)^2+C (AkAi)2+C ,连续选三者的贡献是 ( A j − A k ) 2 + ( A j − A i ) 2 + 2 C (A_j-A_k)^2+(A_j-A_i)^2+2C (AjAk)2+(AjAi)2+2C,那么
( A j − A k ) 2 + ( A j − A i ) 2 + 2 C = 2 A j 2 + A i 2 + A k 2 − 2 A j ( A i + A k ) + 2 C = A i 2 + A k 2 − 2 A j ( A i + A k − A j ) + 2 C (A_j-A_k)^2+(A_j-A_i)^2+2C\\ =2A_j^2+A_i^2+A_k^2-2A_j(A_i+A_k)+2C\\ =A_i^2+A_k^2-2A_j(A_i+A_k-A_j)+2C (AjAk)2+(AjAi)2+2C=2Aj2+Ai2+Ak22Aj(Ai+Ak)+2C=Ai2+Ak22Aj(Ai+AkAj)+2C

d = A j − A k > 0 d=A_j-A_k>0 d=AjAk>0 ,得
A i 2 + A k 2 − 2 ( A k + d ) ( A i − d ) + 2 C = A i 2 + A k 2 − 2 A k A i + d ( A k − A i ) + d 2 + 2 C > A i 2 + A k 2 − 2 A k A i + C = ( A k − A i ) 2 + C A_i^2+A_k^2-2(A_k+d)(A_i-d)+2C\\ =A_i^2+A_k^2-2A_kA_i+d(A_k-A_i)+d^2+2C\\> A_i^2+A_k^2-2A_kA_i+C\\= (A_k-A_i)^2+C Ai2+Ak22(Ak+d)(Aid)+2C=Ai2+Ak22AkAi+d(AkAi)+d2+2C>Ai2+Ak22AkAi+C=(AkAi)2+C

所以这种情况下多选一个 j j j 一定更优。


接下来无视上面的条件,直接求最大值,不难想到动态规划,令 d p [ i ] dp[i] dp[i] 为考虑前 i i i 个位置的贡献,此时 i i i 被选中的最大值:
d p [ i ] = max ⁡ j < i { d p [ j ] + A j 2 − 2 A i ⋅ A j } + A i 2 + C dp[i]=\max_{j<i}\{dp[j]+A_j^2-2A_i\cdot A_j\}+A_i^2+C dp[i]=j<imax{dp[j]+Aj22AiAj}+Ai2+C

大括号中是 k x + b kx+b kx+b 的形式,故可以用李超树维护凸包,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

正解好像是二分和斜率优化,同时维护左凸包和右凸包……

啧,有必要这么复杂么?

当你发现怎么都得带个 log ⁡ \log log 时,不是直接码个李超树更方便快捷吗?

CODE

注意边界最值等细节。

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<random>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 1000005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB long double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
//#define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

int n,m,s,o,k;
int a[MAXN],st[MAXN],tp;
LL dp[MAXN],Y[MAXN];
struct it{
	int k; LL b;
	it(){k=0;b=-4e18;}
	it(int K,LL B) {k=K;b=B;}
	LL F(int x) {return k*1ll*x+b;}
}tre[MAXN<<2];
void addtree(int a,int al,int ar,it st) {
	LL l2 = st.F(al),r2 = st.F(ar);
	LL l1 = tre[a].F(al),r1 = tre[a].F(ar);
	if(l2 >= l1 && r2 >= r1) {tre[a] = st;return ;}
	if(l1 >= l2 && r1 >= r2) return ;
	int md = (al + ar) >> 1;
	if(tre[a].F(md) < st.F(md)) swap(tre[a],st),swap(l1,l2),swap(r1,r2);
	if(l1 < l2) addtree(a<<1,al,md,st);
	if(r1 < r2) addtree(a<<1|1,md+1,ar,st);
	return ;
}
LL query(int a,int x,int al,int ar) {
	if(al > x || ar < x) return (LL)-4e18;
	LL rs = tre[a].F(x);
	if(al < ar) {
		int md = (al + ar) >> 1;
		rs = max(rs,max(query(a<<1,x,al,md),query(a<<1|1,x,md+1,ar)));
	}return rs;
}
int main() {
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	n = read();
	LL m = read();
	int nn = MAXN-5;
	for(int i = 1;i <= n;i ++) {
		a[i] = read();
		dp[i] = max(query(1,a[i],1,nn) + a[i]*1ll*a[i],0ll);
		addtree(1,1,nn,it(-2*a[i],dp[i]+a[i]*1ll*a[i]+m));
	}
	LL ans = 0;
	int mx = 0;
	for(int i = n;i > 0;i --) {
		mx = max(mx,a[i]);
		if(a[i] >= mx) ans = max(ans,dp[i]);
	}
	AIput(ans,'\n');
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值