题解 P10144【[WC/CTS2024] 水镜】

题目描述

给定一个长度为 n 的正整数序列 h1,h2,,hn,求满足以下所有条件的
二元组 (u,v) 的数量:

  • 1u<vn,且 u,v 为整数;
  • 存在一个正实数 L 以及一个长度为 (vu+1) 的序列 ru,ru+1,,rv 满足以下
    所有条件:
    • uiv,记 hi=2Lhi,则 ri{hi,hi},特别地,当 hi=hi 时,ri=hi
    • ui<v,ri<ri+1
  • 2n5×105
  • 1in,1hi1012

solution

pi=max(hi,2Lhi),明显 piL,我们将 ri 的选择分成 <L 的和 L 的两段,<L 的段必然是 min(hi,2Lhi) 单调升,也就是 max(hi,2Lhi) 单调减;L 的段是 pi 单调升;所以最后刻画得到,区间 [l,r] 合法,当且仅当存在 L,t,使得 pl>pl+1>>pt?pt+1<pt+2<<pr。这里你理解一下当 L=ht=ht+1 的时候是个什么东西,你使得 L 向下移动 ϵ 就行,因为原来 pt1>pt 所以移动之后也会满足这个性质。所以 pt?pt+1 这个问号是什么都可以。

反着写一遍条件即为“区间 [l,r] 中不存在 t 使得 pt1ptpt+1”(存在这个 t 就一定不合法,合法一定找不到 t)。这个条件也比较严重,我们考虑 pipi+1 是什么时候成立的。

  • pi=pi+1 时你想象一下就是 L=(hi+hi+1)/2 是合法的。
  • hi=hi+1 时,pi=pi+1 这个事实永恒不变。
  • hi<hi+1 时,L 的减少导致 pi 减少,所以当 L(hi+hi+1)/2pipi+1
  • hi>hi+1 时,L 的减少导致 pi+1 减少,所以当 L(hi+hi+1)/2pipi+1
  • (实现时把它当做是 2L 的限制去掉 /2

这导出一个很严重的做法,即考虑固定 l,向右扫 r,每次把 [l+1,r1] 这个区间的 i 对应的 L 不能选的区间并起来,如果并集不是全集,我们就能找到这样的 L,它就是一个合法区间。

这里,这个区间的 i 对应的 L 不能选的区间,仔细推敲一下发现要么是空,要么是前后缀,要么是全集。大致证明是说,如果有一边的 h 相等马上结束;若连续三个 h 单调,则发现这段前缀和后缀交出来,因为单调,所以是空集(可以看着代码理解一下);否则是前缀之间的交或者后缀之间的交。有了这样的性质可以简化代码。

抛开以上这些东西不谈我们不难发现一个区间合法那么它的子区间合法,说明它可以双指针。

【模板】不删除双指针 - caijianhong - 博客园 (cnblogs.com)

code

#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
typedef long long LL;
constexpr LL inf = 1e18;
struct node {
  LL l, r;
  bool empty() { return l > r; }
};
node cap(node a, node b) {
  if (a.empty() || b.empty()) return node{inf, -inf};
  if (a.l > b.l) swap(a, b);
  else if (a.l == b.l && a.r > b.r) swap(a, b);
  debug("cap([%lld, %lld], [%lld, %lld]) = [%lld, %lld]\n", a.l, a.r, b.l, b.r, b.l, a.r);
  return node{b.l, a.r};
}
node merge(node a, node b) {
  return node{min(a.l, b.l), max(a.r, b.r)};
}
node append(node a, node b) {
  if (!b.empty()) {
    if (b.l == -inf) a.r = max(a.r, b.r);
    if (b.r == inf) a.l = min(a.l, b.l);
  }
  return a;
}
int n;
LL h[500010];
node I[500010], E = node{inf + 1, -inf - 1}; // E 是单位元
int main() {
#ifndef LOCAL
  cin.tie(nullptr)->sync_with_stdio(false);  
#endif
  cin >> n;
  for (int i = 1; i <= n; i++) cin >> h[i];
  for (int i = 2; i < n; i++) {
    node L = h[i - 1] < h[i] ? node{-inf, h[i - 1] + h[i]} : node{h[i - 1] + h[i], inf};
    if (h[i - 1] == h[i]) L = node{-inf, inf};
    node R = h[i] < h[i + 1] ? node{h[i] + h[i + 1], inf} : node{-inf, h[i] + h[i + 1]};
    if (h[i] == h[i + 1]) R = node{-inf, inf};
    I[i] = cap(L, R);
    debug("I[%d] = [%lld, %lld]\n", i, I[i].l, I[i].r);
    if (!I[i].empty()) assert(I[i].l == -inf || I[i].r == inf);
    I[i] = append(E, I[i]);
  }
  LL ans = n - 1;
  static node rel[500010], rer;
  for (int r = 2, l, mid; r < n; ) {
    for (mid = l = r, rel[l + 1] = rer = E; l > 1; --l) {
      rel[l] = merge(I[l], rel[l + 1]);
      if (!rel[l].empty()) break;
    }
    // [l, r] is invaild or l == 1
    for (++l; ans += r - l + 1, ++r < n; ) {
      rer = merge(rer, I[r]);
      while (l <= mid && !merge(rel[l], rer).empty()) ++l;
      if (l > mid) break;
    }
  }
  cout << ans << endl;
  return 0;
}

posted @   caijianhong  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示