CodeForces 1039E. Summer Oenothera Exhibition

链接:

link

题意:

给一个长度为 n n 的序列,q 次询问,每次给一个 ki k i ,问最少将序列划分成多少段,满足每一段的极差不超过 wki w − k i

题解:

这个问题是可以贪心的,所以可以对于一个起始位置,可以暴力或者倍增找它的下一个位置 nexti n e x t i 。为了方便我们将询问离线,按照 wki w − k i 递增的顺序处理。

考虑维护每个点 i i 向后跳,第一次跳到一个大于 i+a 的位置时跳了多少步和跳到了哪。每个点的后继改变次数不超过 a a 次,每次改变可以 O(a) 地维护信息,而对于 nexti>i+a n e x t i > i + a 的位置,我们用倍增暴力找,显然不会找超过 O(na) O ( n a ) 次,这一部分的复杂度是 O(na2+qnlogna) O ( n a 2 + q n log ⁡ n a )

由于倍增复杂度过高,考虑降低倍增的次数,对于 nextii+b n e x t i ≤ i + b 的位置,我们暴力维护 nexti n e x t i ,因为单次改变代价是 O(1) O ( 1 ) 的,而倍增次数降到了 O(nb) O ( n b ) 次,所以总复杂度变为 O(na2+nb+nqa+nqlognb) O ( n a 2 + n b + n q a + n q log ⁡ n b ) ,假设 n,q n , q 同阶,取 a=n13,b=n23 a = n 1 3 , b = n 2 3 则总复杂度为 O(n53+n43logn) O ( n 5 3 + n 4 3 log ⁡ n )

代码:

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m, q;
  scanf("%d %d %d", &n, &m, &q);
  int log_n = 0;
  while (1 << log_n <= n) {
    ++log_n;
  }
  int jump_small = pow(n, 1.0 / 3), jump_large = pow(n, 2.0 / 3);

  vector<vector<int>> rmq_min(log_n, vector<int> (n)), rmq_max(log_n, vector<int> (n));
  vector<int> a(n), next(n), jump(n), times(n), now_min(n), now_max(n);
  priority_queue<pair<int, int>> heap;
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
    rmq_min[0][i] = rmq_max[0][i] = a[i];
    next[i] = i + 1;
    jump[i] = min(i + jump_small, n);
    times[i] = jump[i] - i;
  }
  for (int i = 0; i < n - 1; ++i) {
    now_min[i] = min(a[i], a[i + 1]);
    now_max[i] = max(a[i], a[i + 1]);
    heap.push(make_pair(now_min[i] - now_max[i], i));
  }
  for (int i = 1; i < log_n; ++i) {
    for (int j = 0; j + (1 << i) <= n; ++j) {
      rmq_min[i][j] = min(rmq_min[i - 1][j], rmq_min[i - 1][j + (1 << i - 1)]);
      rmq_max[i][j] = max(rmq_max[i - 1][j], rmq_max[i - 1][j + (1 << i - 1)]);
    }
  }

  vector<int> last(n);
  vector<bool> visit(n);
  auto update = [&](int x, int limit) {
    while (next[x] < n && next[x] - x <= jump_large && now_max[x] - now_min[x] <= limit) { 
      ++next[x];
      if (next[x] < n) {
        now_max[x] = max(now_max[x], a[next[x]]);
        now_min[x] = min(now_min[x], a[next[x]]);
      }
    }
    if (next[x] < n && next[x] - x <= jump_small) {
      heap.push(make_pair(now_min[x] - now_max[x], x));
    }

    vector<int> stack;
    jump[x] = x;
    times[x] = 0;
    stack.push_back(jump[x]);
    while (jump[x] < n && next[jump[x]] - x <= jump_small) {
      jump[x] = next[jump[x]];
      ++times[x];
      stack.push_back(jump[x]);
    }

    last[x] = stack.size();
    visit[x] = true;
    for (int i = x - 1; i >= x - jump_small && ~i; --i) {
      if (next[i] < n && visit[next[i]]) {
        while (stack.back() - i > jump_small) {
          stack.pop_back();
        }
        jump[i] = stack.back();
        times[i] = times[next[i]] + 1 + stack.size() - last[next[i]];
        last[i] = stack.size();
        visit[i] = true;
      }
    }
    for (int i = x; i >= x - jump_small && ~i; --i) {
      visit[i] = false;
    }
  };

  auto query = [&](int limit) {
    while (!heap.empty() && -heap.top().first <= limit) {
      int x = heap.top().second;
      heap.pop();
      update(x, limit);
    }
    int answer = -1, x = 0;
    while (x < n) {
      int dist = next[x] - x;
      if (dist <= jump_small) {
        answer += times[x];
        x = jump[x];
      } else {
        while (next[x] < n && next[x] - x <= jump_large && now_max[x] - now_min[x] <= limit) {
          ++next[x];
          if (next[x] < n) {
            now_max[x] = max(now_max[x], a[next[x]]);
            now_min[x] = min(now_min[x], a[next[x]]);
          }
        }
        dist = next[x] - x;
        if (dist <= jump_large) {
          ++answer;
          x = next[x];
        } else {
          ++answer;
          int current_min = a[x], current_max = a[x];
          for (int i = log_n - 1; ~i; --i) {
            if (x + (1 << i) <= n) {
              int next_min = min(current_min, rmq_min[i][x]);
              int next_max = max(current_max, rmq_max[i][x]);
              if (next_max - next_min <= limit) {
                current_min = next_min;
                current_max = next_max;
                x += 1 << i;
              }
            }
          }
        }
      }
    }
    return answer;
  };

  vector<pair<int, int>> queries(q);
  vector<int> answer(q);
  for (int i = 0; i < q; ++i) {
    scanf("%d", &queries[i].first);
    queries[i].first = m - queries[i].first;
    queries[i].second = i;
  }
  sort(queries.begin(), queries.end());
  for (auto p : queries) {
    answer[p.second] = query(p.first);
  }
  for (int i = 0; i < q; ++i) {
    printf("%d\n", answer[i]);
  }
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值