题目链接:点我啊╭(╯^╰)╮
题目大意:
n
n
n 个数字,每个数字为
s
[
i
]
s[i]
s[i]
每次选取一串数字,得到
s
0
×
t
2
s_0 \times t^2
s0×t2
s
0
s_0
s0 为这一串数字的任意一个,
t
t
t 为这串数字有
t
t
t 个
s
0
s_0
s0
求全部选取完之后的最大值
解题思路:
d
p
[
i
]
=
d
p
[
j
−
1
]
+
s
[
i
]
∗
c
a
l
(
j
,
i
)
dp[i] = dp[j-1] + s[i] * cal(j, i)
dp[i]=dp[j−1]+s[i]∗cal(j,i)
明显决策点
s
[
i
]
=
s
[
j
]
s[i] = s[j]
s[i]=s[j]
c
a
l
cal
cal 用前缀计算,设
a
[
i
]
a[i]
a[i] 为
s
i
s_i
si 这个数字是相同数字里的第几个
则
d
p
[
i
]
=
d
p
[
j
−
1
]
+
s
[
i
]
∗
(
a
[
i
]
−
a
[
j
]
+
1
)
2
dp[i] = dp[j-1] + s[i] * (a[i] - a[j] + 1) ^ 2
dp[i]=dp[j−1]+s[i]∗(a[i]−a[j]+1)2
复杂度:
O
(
n
2
)
O(n^2)
O(n2)
但是这道题的决策是倒过来的!!!因此要在队尾操作!!!
斜率优化
d
p
[
i
]
=
d
p
[
j
−
1
]
+
s
i
×
a
i
2
−
2
s
i
a
i
a
j
+
s
i
a
j
2
+
2
s
i
a
i
−
2
s
i
a
j
+
s
i
dp[i] = dp[j-1] + s_i \times a_i ^ 2 - 2s_i a_i a_j + s_i a_j^2 + 2 s_i a_i - 2s_i a_j + s_i
dp[i]=dp[j−1]+si×ai2−2siaiaj+siaj2+2siai−2siaj+si
发现有与
i
i
i 和
j
j
j 都相关的项,则提取出来,把只和
i
i
i 有关的项分离出去
d
p
[
i
]
=
−
2
s
i
a
i
a
j
+
d
p
[
j
−
1
]
+
s
i
a
j
2
−
2
s
i
a
j
+
.
.
.
dp[i] = - 2 s_i a_i a_j + dp[j-1] + s_i a_j ^ 2 - 2 s_i a_j + ...
dp[i]=−2siaiaj+dp[j−1]+siaj2−2siaj+...
设
j
<
k
<
i
j<k<i
j<k<i,
d
p
[
i
]
[
j
]
<
=
d
p
[
i
]
[
k
]
dp[i][j] <= dp[i][k]
dp[i][j]<=dp[i][k],
a
k
>
a
j
a_k>a_j
ak>aj,得
(
d
p
[
j
−
1
]
+
s
i
a
j
2
−
2
s
i
a
j
)
−
(
d
p
[
k
−
1
]
+
s
i
a
k
2
−
2
s
i
a
k
)
a
j
−
a
k
≥
2
s
i
a
i
\frac{(dp[j-1] + s_i a_j ^ 2 - 2 s_i a_j) - (dp[k-1] + s_i a_k ^ 2 - 2 s_i a_k)}{a_j - a_k} ≥ 2 s_i a_i
aj−ak(dp[j−1]+siaj2−2siaj)−(dp[k−1]+siak2−2siak)≥2siai
符号为
≥
≥
≥,维护上凸包,斜率递减,由于
2
s
i
a
i
2 s_i a_i
2siai 是递增,所以在队尾操作
对于一个
s
i
si
si 而言,
2
s
i
a
i
2 s_i a_i
2siai 是单调的,因此不需要二分
时间复杂度:
O
(
n
)
O(n)
O(n)
决策单调
发现平方的增长是很快的
若
i
i
i 的决策点有
j
1
j_1
j1 和
j
2
j_2
j2,
j
1
<
j
2
j_1 < j_2
j1<j2,
a
[
j
1
]
<
a
[
j
2
]
a[j_1] < a[j_2]
a[j1]<a[j2],
d
p
[
i
]
(
j
1
)
>
d
p
[
i
]
(
j
2
)
dp[i](j_1) > dp[i](j_2)
dp[i](j1)>dp[i](j2)
那么随着
i
i
i 的增大,
d
p
[
i
]
(
j
1
)
dp[i](j_1)
dp[i](j1) 增长的肯定要比
d
p
[
i
]
(
j
2
)
dp[i](j_2)
dp[i](j2) 快
因此
j
2
j_2
j2 可以直接删掉,满足决策单调
注意这里的决策单调是倒序单调的
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
斜率优化:
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 1e5 + 5;
int n;
ll s[maxn], a[maxn];
ll num[maxn], dp[maxn];
vector <int> q[maxn];
ll Y(int i){
return dp[i-1] + s[i] * a[i] * a[i] - 2 * s[i] * a[i];
}
ll X(int i){
return a[i];
}
double slope(int i, int j){
return 1.0 * (Y(i) - Y(j)) / (X(i) - X(j));
}
#define sz q[s[i]].size()
#define t1 q[t][sz-1]
#define t2 q[t][sz-2]
ll cal(int i, int j){
return dp[j-1] + s[i] * (a[i] - a[j] + 1) * (a[i] - a[j] + 1);
}
signed main() {
scanf("%d", &n);
for(int i=1; i<=n; i++) {
scanf("%lld", s+i);
a[i] = ++num[s[i]];
}
for(int i=1; i<=n; i++){
int t = s[i];
while(sz>1 && slope(t1, i)>=slope(t2, t1)) q[t].pop_back();
q[t].push_back(i);
while(sz>1 && slope(t2, t1)<2*s[i]*a[i]) q[t].pop_back();
// while(sz>1 && cal(i, t1)<=cal(i, t2)) q[t].pop_back();
dp[i] = cal(i, t1);
}
printf("%lld\n", dp[n]);
}
决策单调:
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 1e5 + 5;
int n;
ll a[maxn], s[maxn];
ll dp[maxn], num[maxn];
vector <int> q[maxn];
#define sz q[t].size()
#define t1 q[t][sz-1]
#define t2 q[t][sz-2]
ll cal(int i, int j){
return dp[i-1] + 1ll * j * j * s[i];
}
int ck(int i, int j){
int l = max(a[i], a[j]), r = n, mid;
while(l <= r){
mid = l + r >> 1;
if(cal(i, mid-a[i]+1)>=cal(j, mid-a[j]+1)) r = mid - 1;
else l = mid + 1;
}
return l;
}
signed main() {
scanf("%d", &n);
for(int i=1; i<=n; i++){
scanf("%lld", s+i);
a[i] = ++num[s[i]];
}
for(int i=1; i<=n; i++){
int t = s[i];
while(sz>1 && ck(t2, t1)<=ck(t1, i)) q[t].pop_back();
q[t].push_back(i);
while(sz>1 && ck(t2, t1)<=a[i]) q[t].pop_back();
dp[i] = cal(t1, a[i] - a[t1] + 1);
}
printf("%lld\n", dp[n]);
}