背景
灵感源于知乎
有一天,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 1≤p1<p2<...<pm−1<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,m−1] , B j B_j Bj 为数列 A A A 中区间 ( p j − 1 , p j ] (p_{j-1},p_j] (pj−1,pj] 的最大值, B m B_m Bm 为数列 A A A 中区间 ( p m − 1 , n ] (p_{m-1},n] (pm−1,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=2∑m((Bi−Bi−1)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} 2≤n≤106,1≤Ai≤106,0≤C≤1010 。
题解
不难把题面转换一下:
对于数列 A A A ,你需要选择若干个下标 1 ≤ p 1 < p 2 < . . . < p m ≤ n 1\leq p_1<p_2<...<p_{m}\leq n 1≤p1<p2<...<pm≤n ,令 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=2∑m((Api−Api−1)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
(Ak−Ai)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
(Aj−Ak)2+(Aj−Ai)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
(Aj−Ak)2+(Aj−Ai)2+2C=2Aj2+Ai2+Ak2−2Aj(Ai+Ak)+2C=Ai2+Ak2−2Aj(Ai+Ak−Aj)+2C
令
d
=
A
j
−
A
k
>
0
d=A_j-A_k>0
d=Aj−Ak>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+Ak2−2(Ak+d)(Ai−d)+2C=Ai2+Ak2−2AkAi+d(Ak−Ai)+d2+2C>Ai2+Ak2−2AkAi+C=(Ak−Ai)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]+Aj2−2Ai⋅Aj}+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;
}