UER #7

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;
}
posted @ 2024-10-09 10:10  Yaosicheng124  阅读(1)  评论(0编辑  收藏  举报