LuoguP5504 [JSOI2011]柠檬
题目描述
Solution
容易发现一个性质:每一段划分区间的首尾两个元素相同。
因为倘若不相同的话其中至少一个元素也就不产生贡献,将其划分在其他区间一定不会变劣。
因此就可以写出一个简单的
O
(
n
2
)
O(n^2)
O(n2)的
d
p
dp
dp。
f
i
=
f
j
−
1
+
(
s
i
−
s
j
+
1
)
2
(
a
i
=
a
j
)
f_i=f_{j-1}+(s_i-s_j+1)^2\;\;\;\;\;\;\;(a_i=a_j)
fi=fj−1+(si−sj+1)2(ai=aj)
其中
s
i
s_i
si表示在
i
i
i之前的与
i
i
i相同的元素的个数。
考虑决策单调性:
设有
j
1
<
j
2
<
i
j_1<j_2<i
j1<j2<i,
a
j
1
=
a
j
2
a_{j_1}=a_{j_2}
aj1=aj2,令
g
e
t
a
n
s
(
x
,
y
)
getans(x,y)
getans(x,y)表示
f
x
−
1
+
(
s
y
−
s
x
+
1
)
2
f_{x-1}+(s_y-s_x+1)^2
fx−1+(sy−sx+1)2。
若 g e t a n s ( j 1 , i ) ≥ g e t a n s ( j 2 , i ) getans(j_1,i)\geq getans(j_2,i) getans(j1,i)≥getans(j2,i),则对于所有 i ′ ≥ i i'\geq i i′≥i,都有 g e t a n s ( j 1 , i ′ ) ≥ g e t a n s ( j 2 , i ′ ) getans(j_1,i')\geq getans(j_2,i') getans(j1,i′)≥getans(j2,i′),因为两者的增长都是平方级别的。
因此我们可以用单调栈维护这一过程。
对于每一种
a
i
a_i
ai开一个单调栈,我们希望的是保证栈中元素的
g
e
t
a
n
s
(
.
.
.
,
i
)
getans(...,i)
getans(...,i)始终递增。每次如果发现栈顶元素没有它下面一个元素优,就弹栈。
但这并不是完全正确的,随着 i i i的后移,因为前面的元素增长快,所以可能存在一个 j 1 < j 2 < j 3 < i j_1<j_2<j_3<i j1<j2<j3<i,使得 g e t a n s ( j 1 , i ) > g e t a n s ( j 3 , i ) getans(j_1,i)>getans(j_3,i) getans(j1,i)>getans(j3,i),但 g e t a n s ( j 2 , i ) < g e t a n s ( j 3 , i ) getans(j_2,i)<getans(j_3,i) getans(j2,i)<getans(j3,i)。
因此我们还需要保证栈中每一个元素 x x x超过上面一个元素 y y y的时间小于 y y y超过它上面的元素 z z z的时间。
这个某个元素超过另一个元素的时间可以二分求得。
所以我们维护单调栈时额外添加一个判断 g e t a n s ( s t a c k [ t o p − 2 ] ) > g e t a n s ( s t a c k [ t o p − 1 ] ) getans(stack[top-2])>getans(stack[top-1]) getans(stack[top−2])>getans(stack[top−1])的时间是否比 g e t a n s ( s t a c k [ t o p − 1 ] ) > g e t a n s ( s t a c k [ t o p ] ) getans(stack[top-1])>getans(stack[top]) getans(stack[top−1])>getans(stack[top])的时间短即可。
时间复杂度 O ( n l g n ) O(nlgn) O(nlgn)。
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second
using namespace std;
template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }
typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;
const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=600005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
int f=1,x=0; char c=getchar();
while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
return x*f;
}
ll f[MAXN];
vector<int> V[MAXN];
int a[MAXN],s[MAXN],cnt[MAXN],n;
ll Getans(int x,int y) { return f[x-1]+1ll*a[x]*y*y; }
ll getans(int x,int y) { return f[x-1]+1ll*a[x]*(s[y]-s[x]+1)*(s[y]-s[x]+1); }
int check(int x,int y)
{
int l=s[y],r=n+1;
while (l<r)
{
int mid=(l+r)>>1;
if (Getans(x,mid-s[x]+1)>=Getans(y,mid-s[y]+1)) r=mid;
else l=mid+1;
}
return r;
}
int main()
{
n=read();
for (int i=1;i<=n;i++) s[i]=++cnt[a[i]=read()];
f[0]=0;
for (int i=1,sz;i<=n;i++)
{
sz=V[a[i]].size()-1;
while (sz>=1&&check(V[a[i]][sz-1],V[a[i]][sz])<=check(V[a[i]][sz],i)) V[a[i]].pop_back(),sz--;
V[a[i]].PB(i),sz++;
while (sz>=1&&getans(V[a[i]][sz-1],i)>=getans(V[a[i]][sz],i)) V[a[i]].pop_back(),sz--;
f[i]=getans(V[a[i]][sz],i);
}
printf("%lld\n",f[n]);
return 0;
}