B. 天路
题目描述
在一根数轴上,有 \(N\) 个点 \(A_1,A_2,\dots,A_N\),你要对于 \(\forall 2\le k\le N\),求出 \(\min\limits_{1\le l\le N-k+1} \{\max \limits_{l\le i< j\le l+k-1}\{|A_i-A_j|\}\}\)。
如果对于每个 \(k\),你输出的答案 \(c_k\) 与标准答案 \(\widehat{c_k}\) 的相对误差不超过 \(\% 5\),即 \(|c_k-\widehat{c_k}|\le \widehat{c_k}\cdot \% 5\),那么你的答案将会被接受。
思路
注意到这里允许你的答案有 \(\% 5\) 的误差,所以考虑枚举答案,并求出对应的 \(k\)。由于这里的答案不超过 \(10^6\),并且每次枚举答案 \(x\) 可以直接令 \(x\leftarrow \max(x+1,1.05\cdot x)\),所以只需枚举 \(\log_{1.05} 10^6 \approx 300\) 次。
那么怎么求出 \(k\) 呢?我们用双指针求出能够覆盖的极大区间,使用单调队列维护区间最大/小值即可。
空间复杂度 \(O(N)\),时间复杂度 \(O(N\log_{1.05} V)\),其中 \(V=10^6\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100001, MAXV = 1000001;
int n, a[MAXN], que[2][MAXN], head[2], tail[2], ans[MAXN];
int Solve(int x) {
head[0] = head[1] = 1;
tail[0] = tail[1] = 0;
int ret = 0;
for(int i = 1, j = 0; i <= n; ++i) {
for(; j <= n && (head[0] > tail[0] || head[1] > tail[1] || a[que[0][head[0]]] - a[que[1][head[1]]] <= x); ) {
if(++j <= n) {
for(; head[0] <= tail[0] && a[que[0][tail[0]]] <= a[j]; tail[0]--) {
}
que[0][++tail[0]] = j;
for(; head[1] <= tail[1] && a[que[1][tail[1]]] >= a[j]; tail[1]--) {
}
que[1][++tail[1]] = j;
}
}
ret = max(ret, j - i);
for(; head[0] <= tail[0] && que[0][head[0]] <= i; head[0]++) {
}
for(; head[1] <= tail[1] && que[1][head[1]] <= i; head[1]++) {
}
}
return ret;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
ans[i] = int(1e9) + 1;
}
for(int i = 0; i < 2000000; i = max(i + 1, (int)(1.05l * i))) {
int x = Solve(i);
ans[x] = min(ans[x], i);
}
for(int i = n - 1; i >= 2; --i) {
ans[i] = min(ans[i], ans[i + 1]);
}
for(int i = 2; i <= n; ++i) {
cout << ans[i] << "\n";
}
return 0;
}
C. 套路
题目描述
有一个数列 \(A_1,A_2,\dots,A_N(1\le A_i\le M)\)。令 \(s(l,r)=\min\limits_{l\le i<j\le r}\{|A_i-A_j|\}\),你要求出 \(\max\limits_{1\le l+k-1\le r\le N}\{(r-l)\cdot s(l,r)\}\)。
思路
由于 \(s(l,r)\le \frac{M-1}{r-l}\),所以可以想到根号分治。
对于 \(s(l,r)\le \sqrt M\),我们从小到大枚举 \(s(l,r)\)。我们令 \(lst_{i}\) 为最大的 \(j\) 使得 \(|A_i-A_j|<s(l,r)\)。一开始 \(s(l,r)=1\),那么 \(lst_i\) 就是上次 \(A_i\) 出现的位置,可以轻松求出。后来每次 \(s(l,r)\) 加一,只需将 \(lst_i\) 与 \(A_i-s(l,r)+1,A_i+s(l,r)-1\) 出现的位置去 \(\max\) 即可。接着双指针,用单调队列维护区间 \(lst_i\) 最大值即可。
对于 \(s(l,r)>\sqrt M\),那么 \(r-l+1\le \sqrt M\),所以我们可以使用区间 dp,令 \(dp_{i,l}\) 表示区间长度为 \(i\),左端点为 \(l\) 时的 \(s(i,l)\)。很明显有转移 \(dp_{i,l}=\min(dp_{i-1,l},dp_{i-1,l+1},|A_l-A_{l+i-1}|)\)。这里可以用降维优化。
空间复杂度 \(O(N+M)\),时间复杂度 \(O((N+M)\sqrt M)\)。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 200001, B = 447;
int n, m, k, a[MAXN], pos[MAXN], lst[MAXN], que[MAXN], head, tail, dp[MAXN];
ll ans;
int Solve(int x) {
fill(pos + 1, pos + m + 1, 0);
int ret = 0;
for(int i = 1; i <= n; ++i) {
lst[i] = max({lst[i], (a[i] - x + 1 >= 1 ? pos[a[i] - x + 1] : 0), (a[i] + x - 1 <= m ? pos[a[i] + x - 1] : 0)});
pos[a[i]] = i;
}
head = 1, tail = 0;
for(int i = 1, j = 0; i <= n; ++i) {
for(; j <= n && (head > tail || lst[que[head]] < i); ) {
if(++j <= n) {
for(; head <= tail && lst[que[tail]] <= lst[j]; --tail) {
}
que[++tail] = j;
}
}
ret = max(ret, j - i - 1);
}
return ret;
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> k;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
dp[i] = MAXN;
}
ll A = 0, b = 0;
for(int i = 1; i <= min(m, (MAXN - 1 + B - 1) / B); ++i) {
ll x = Solve(i);
if(x + 1 >= k) {
ans = max(ans, 1ll * x * i);
A = max(A, 1ll * x * i);
}
}
for(int i = 2; i <= min(n, B); ++i) {
for(int l = 1, r = i; r <= n; ++l, ++r) {
dp[l] = min({dp[l], abs(a[l] - a[r]), dp[l + 1]});
if(i >= k) {
ans = max(ans, 1ll * (i - 1) * dp[l]);
b = max(b, 1ll * dp[l] * (i - 1));
}
}
}
cout << ans;
return 0;
}
标签:le,int,tail,MAXN,ans,dp,UER
From: https://www.cnblogs.com/yaosicheng124/p/18453645