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}\) 的條件為:
又因為:
所以結論得證。
然後就可以從前往後列舉 \(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;
}