CF573E Bear and Bowling 題解

下蛋爷發表於2024-08-06

Description

  • 給定一個長度為 \(n\) 的序列 \(a_{1\dots n}\)
  • 你要求一個 \(a\) 的子序列 \(b_{1\dots m}\)(可以為空),使得 \(\sum_{i=1}^m ib_i\) 的值最大。
  • \(n \le 10^5\)\(|a_i| \le 10^7\)

Solution

有一個顯然的 dp 是設 \(f_{i,j}\) 表示前 \(i\) 個數,選 \(j\) 個數的最大值,轉移即為:\(f_{i,j}=\max\left\{f_{i-1,j},f_{i-1,j-1}+j\cdot a_i\right\}\),由於這題時限很大並且 \(n\) 很小,所以這個能過。。。

考慮最佳化。

有一個結論是對於每個 \(i\),都存在一個分界點 \(k_i\),使得對於 \(\forall j<k_i\)\(f_{i,j}=f_{i-1,j}\),對於 \(j\geq k_i\)\(f_{i,j}=f_{i-1,j-1}+j\cdot a_i\)

證明就考慮設 \(g_{i,j}=f_{i,j}-f_{i,j-1}\),那麼 \(f_{i,j}=f_{i-1,j}\) 的充分必要條件為 \(f_{i-1,j}\geq f_{i-1,j-1}+j\cdot a_i\),即 \(\frac{g_{i-1,j}}{j}\geq a_i\)

透過觀察可以發現 \(\frac{g_{i,j}}{j}\) 對於 \(j\) 單調不增,那麼不妨假設 \(\frac{g_{i-1,j}}{j}\) 單調不增,考慮歸納證明 \(g_i\) 也滿足條件。

\(k\) 為滿足 \(\frac{g_{i-1,j}}{j}<a_i\) 的最小的 \(j\),則對於 \(j\in [0,k-1]\)\(g_{i,j}=g_{i-1,j}\)。同時 \(g_{i,k}\) 變為 \(k\cdot a_i\),所以 \(\frac{g_{i,k}}{k}=a_i\leq \frac{g_{i,k-1}}{k-1}\)

對於 \(j>k\)\(g_{i,j}=g_{i-1,j-1}+a_i\),所以對於 \(j>k\) 滿足 \(\frac{g_{i,j}}{j}\geq \frac{g_{i,j+1}}{j+1}\) 的條件為:

\[\begin{aligned} \frac{g_{i-1,j-1}+a_i}{j}&\geq\frac{g_{i-1,j}+a_i}{j+1}\\ g_{i-1,j-1}&\geq\frac{j}{j+1}\cdot g_{i-1,j}-\frac{1}{j+1}\cdot a_i\\ g_{i-1,j-1}-\left(\frac{j}{j+1}\cdot g_{i-1,j}-\frac{1}{j+1}\cdot a_i\right)&\geq 0\\ \end{aligned} \]

又因為:

\[\begin{aligned} LHS\geq&\frac{j-1}{j}\cdot g_{i-1,j}-\frac{j}{j+1}\cdot g_{i-1,j}+\frac{1}{j+1}\cdot a_i\\ =&\frac{j\cdot a_i-g_{i-1,j}}{j(j+1)}\\ \geq& 0 \end{aligned} \]

所以結論得證。

然後就可以從前往後列舉 \(a_i\),維護 \(g\) 陣列,每次相當於是在某個位置插入和將某個字尾區間加某個數,並且需要快速找到分界點,可以用平衡樹維護。

時間複雜度:\(O(n\log n)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5;

int n;
int64_t a[kMaxN];

struct FHQTreap {
  int tot = 0, rt, ch[kMaxN][2], rd[kMaxN], sz[kMaxN];
  int64_t val[kMaxN], rnk[kMaxN], tag1[kMaxN], tag2[kMaxN];
  std::mt19937 rnd;

  int newnode(int64_t x, int64_t y) {
    val[++tot] = x, rnk[tot] = y;
    ch[tot][0] = ch[tot][1] = tag1[tot] = tag2[tot] = 0, rd[tot] = rnd();
    sz[tot] = 1;
    return tot;
  }

  FHQTreap() {
    tot = 0, rnd.seed(std::random_device{}());
    rt = newnode(-1e18, 1);
  }

  void pushup(int x) {
    sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
  }

  void addtag(int x, int64_t v1, int64_t v2) {
    val[x] += v1, rnk[x] += v2, tag1[x] += v1, tag2[x] += v2;
  }

  void pushdown(int x) {
    if (ch[x][0]) addtag(ch[x][0], tag1[x], tag2[x]);
    if (ch[x][1]) addtag(ch[x][1], tag1[x], tag2[x]);
    tag1[x] = tag2[x] = 0;
  }

  int merge(int x, int y) {
    if (!x || !y) return x + y;
    pushdown(x), pushdown(y);
    if (rd[x] < rd[y]) {
      ch[x][1] = merge(ch[x][1], y), pushup(x);
      return x;
    } else {
      ch[y][0] = merge(x, ch[y][0]), pushup(y);
      return y;
    }
  }

  void split(int x, int v, int &a, int &b) { // a : >= v, b : < v
    if (!x) return void(a = b = 0);
    pushdown(x);
    if (val[x] >= rnk[x] * v) {
      a = x, split(ch[x][1], v, ch[a][1], b);
    } else {
      b = x, split(ch[x][0], v, a, ch[b][0]);
    }
    pushup(x);
  }

  void ins(int64_t x) {
    int a, b;
    split(rt, x, a, b);
    if (b) addtag(b, x, 1);
    rt = merge(merge(a, newnode(x * (sz[a] + 1), sz[a] + 1)), b);
  }

  int64_t getval(int x) {
    if (!x) return 0;
    pushdown(x);
    int64_t ret = std::max<int64_t>(val[x], 0);
    return ret + getval(ch[x][0]) + getval(ch[x][1]);
  }
} t;

void dickdreamer() {
  std::cin >> n;
  for (int i = 1; i <= n; ++i) {
    std::cin >> a[i];
    t.ins(a[i]);
  }
  std::cout << t.getval(t.rt) << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}