题目链接:点我啊╭(╯^╰)╮
题目大意:
解题思路:
p
i
≥
a
j
+
i
−
j
−
a
i
p_i ≥ a_j + \sqrt{i-j} - a_i
pi≥aj+i−j−ai,
j
≤
i
j≤i
j≤i
p
i
≥
a
j
+
j
−
i
−
a
i
p_i ≥ a_j + \sqrt{j-i} - a_i
pi≥aj+j−i−ai,
j
≥
i
j≥i
j≥i
只讨论第一个,可转换为:
p
i
=
m
i
n
(
a
j
+
i
−
j
)
−
a
i
p_i = min(a_j + \sqrt{i-j}) - a_i
pi=min(aj+i−j)−ai
设
y
=
a
j
+
i
−
j
y = a_j + \sqrt{i-j}
y=aj+i−j ,看成是关于
i
i
i 的曲线,一共有
j
j
j 条
画出图后,
i
i
i 的答案就是在
j
≤
i
j≤i
j≤i 的所有曲线里,找最大值
发现两条曲线最多只有一个交点
也就是两条曲线,第二条比第一条更优后,第一条就不会再出现在答案里了
那么就可以求出所有的交点,设
k
[
i
]
k[i]
k[i] 为
q
[
i
]
q[i]
q[i] 与
q
[
i
+
1
]
q[i+1]
q[i+1] 的交点的横坐标
c
a
l
(
i
,
j
)
cal(i,j)
cal(i,j) 表示第
j
j
j 条曲线,横坐标为
i
i
i 的值
若
c
a
l
(
k
[
t
a
i
l
−
1
]
,
q
[
t
a
i
l
]
)
<
c
a
l
(
k
[
t
a
i
l
−
1
]
,
i
)
cal(k[tail-1], q[tail])<cal(k[tail-1], i)
cal(k[tail−1],q[tail])<cal(k[tail−1],i),则
q
[
t
a
i
l
]
q[tail]
q[tail] 这条曲线可以直接去掉了
注意这里有一个很重要的原因:
j
j
j 较大的曲线增速比
j
j
j 较小的曲线快!
具体原因画图即可,这里不作详解
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
注意,上面大概讲述了这题是可以用决策单调来优化的(或者你可以直接用四边形不等式来证)
发现用代码写出来之后,和普通的二分栈写法几乎一模一样!
唯一的不同点在于常规写法是二分计算最优决策来决定是否淘汰
而这道题可以直接代入计算当前队尾是否需要淘汰,这仅仅是这道题的特例!
把淘汰的那一行换成二分的形式,也是可以直接过的!!(可能要加读入挂或者开O2)
还有另一种方法
由经验证明可得这是具有决策单调性的
且
p
[
i
]
p[i]
p[i] 彼此之间无关
直接分治解决即可
时间复杂度:
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;
const int maxn = 5e5 + 5;
int n, a[maxn], k[maxn], q[maxn];
double p[maxn];
inline double cal(int x, int y){
return sqrt(x-y) + a[y];
}
int gao(int x, int y){
int l = y, r = n, mid;
while(l <= r){
mid = l + r >> 1;
if(cal(mid, x) <= cal(mid, y)) r = mid - 1;
else l = mid + 1;
}
return l;
}
void solve(){
int head = 1, tail = 0;
for(int i=1; i<=n; i++){
while(head<tail && cal(k[tail-1], q[tail])<cal(k[tail-1], i)) tail--;
// while(head<tail && k[tail-1]>=gao(q[tail], i)) tail--; // 常规写法
k[tail] = gao(q[tail], i); q[++tail] = i;
while(head<tail && k[head]<=i) head++;
p[i] = max(p[i], cal(i, q[head]));
}
}
int main() {
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", a+i);
solve();
for(int i=1, j=n; i<j; i++, j--) swap(a[i], a[j]), swap(p[i], p[j]);
solve();
for(int i=n; i; i--) printf("%d\n", (int)ceil(p[i]) - a[i]);
}
分治:
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
int n, a[maxn];
double dp1[maxn], dp2[maxn];
void solve1(int l, int r, int x, int y) {
if(x > y) return;
int mid = x + y >> 1, pos;
for(int i=l; i<=min(r, mid); i++) {
double tmp = sqrt(double(mid - i)) + a[i];
if(tmp > dp1[mid]) {
dp1[mid] = tmp;
pos = i;
}
}
solve1(l, pos, x, mid-1);
solve1(pos, r, mid+1, y);
}
void solve2(int l, int r, int x, int y) {
if(x > y) return;
int mid = x + y >> 1, pos;
for(int i=r; i>=max(l, mid); i--) {
double tmp = sqrt(double(i - mid)) + a[i];
if(tmp > dp2[mid]) {
dp2[mid] = tmp;
pos = i;
}
}
solve2(l, pos, x, mid-1);
solve2(pos, r, mid+1, y);
}
int main() {
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", a+i);
solve1(1, n, 1, n);
solve2(1, n, 1, n);
for(int i=1; i<=n; i++) printf("%d\n", (int)ceil(max(dp1[i], dp2[i])) - a[i]);
}