Description给定一个长度为 n n n 的序列 a 1 … n a_{1\dots n} a 1 … n 。 你要求一个 a a a 的子序列 b 1 … m b_{1\dots m} b 1 … m (可以为空),使得 ∑ i = 1 m i b i \sum_{i=1}^m ib_i ∑ i = 1 m i b i 的值最大。 n ≤ 1 0 5 n \le 10^5 n ≤ 1 0 5 ,∣ a i ∣ ≤ 1 0 7 |a_i| \le 10^7 ∣ a i ∣ ≤ 1 0 7 。 Solution有一个显然的 dp 是设 f i , j f_{i,j} f i , j 表示前 i i i 个数,选 j j j 个数的最大值,转移即为:f i , j = max { f i − 1 , j , f i − 1 , j − 1 + j ⋅ a i } f_{i,j}=\max\left\{f_{i-1,j},f_{i-1,j-1}+j\cdot a_i\right\} f i , j = max { f i − 1 , j , f i − 1 , j − 1 + j ⋅ a i } ,由于这题时限很大并且 n n n 很小,所以这个能过。。。
考虑优化。
有一个结论是对于每个 i i i ,都存在一个分界点 k i k_i k i ,使得对于 ∀ j < k i \forall j<k_i ∀ j < k i ,f i , j = f i − 1 , j f_{i,j}=f_{i-1,j} f i , j = f i − 1 , j ,对于 j ≥ k i j\geq k_i j ≥ k i ,f i , j = f i − 1 , j − 1 + j ⋅ a i f_{i,j}=f_{i-1,j-1}+j\cdot a_i f i , j = f i − 1 , j − 1 + j ⋅ a i 。
证明就考虑设 g i , j = f i , j − f i , j − 1 g_{i,j}=f_{i,j}-f_{i,j-1} g i , j = f i , j − f i , j − 1 ,那么 f i , j = f i − 1 , j f_{i,j}=f_{i-1,j} f i , j = f i − 1 , j 的充分必要条件为 f i − 1 , j ≥ f i − 1 , j − 1 + j ⋅ a i f_{i-1,j}\geq f_{i-1,j-1}+j\cdot a_i f i − 1 , j ≥ f i − 1 , j − 1 + j ⋅ a i ,即 g i − 1 , j j ≥ a i \frac{g_{i-1,j}}{j}\geq a_i j g i − 1 , j ≥ a i 。
通过观察可以发现 g i , j j \frac{g_{i,j}}{j} j g i , j 对于 j j j 单调不增,那么不妨假设 g i − 1 , j j \frac{g_{i-1,j}}{j} j g i − 1 , j 单调不增,考虑归纳证明 g i g_i g i 也满足条件。
设 k k k 为满足 g i − 1 , j j < a i \frac{g_{i-1,j}}{j}<a_i j g i − 1 , j < a i 的最小的 j j j ,则对于 j ∈ [ 0 , k − 1 ] j\in [0,k-1] j ∈ [ 0 , k − 1 ] ,g i , j = g i − 1 , j g_{i,j}=g_{i-1,j} g i , j = g i − 1 , j 。同时 g i , k g_{i,k} g i , k 变为 k ⋅ a i k\cdot a_i k ⋅ a i ,所以 g i , k k = a i ≤ g i , k − 1 k − 1 \frac{g_{i,k}}{k}=a_i\leq \frac{g_{i,k-1}}{k-1} k g i , k = a i ≤ k − 1 g i , k − 1 。
对于 j > k j>k j > k ,g i , j = g i − 1 , j − 1 + a i g_{i,j}=g_{i-1,j-1}+a_i g i , j = g i − 1 , j − 1 + a i ,所以对于 j > k j>k j > k 满足 g i , j j ≥ g i , j + 1 j + 1 \frac{g_{i,j}}{j}\geq \frac{g_{i,j+1}}{j+1} j g i , j ≥ j + 1 g i , j + 1 的条件为:
g i − 1 , j − 1 + a i j ≥ g i − 1 , j + a i j + 1 g i − 1 , j − 1 ≥ j j + 1 ⋅ g i − 1 , j − 1 j + 1 ⋅ a i g i − 1 , j − 1 − ( j j + 1 ⋅ g i − 1 , j − 1 j + 1 ⋅ a i ) ≥ 0 \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} j g i − 1 , j − 1 + a i g i − 1 , j − 1 g i − 1 , j − 1 − ( j + 1 j ⋅ g i − 1 , j − j + 1 1 ⋅ a i ) ≥ j + 1 g i − 1 , j + a i ≥ j + 1 j ⋅ g i − 1 , j − j + 1 1 ⋅ a i ≥ 0
又因为:
L H S ≥ j − 1 j ⋅ g i − 1 , j − j j + 1 ⋅ g i − 1 , j + 1 j + 1 ⋅ a i = j ⋅ a i − g i − 1 , j j ( j + 1 ) ≥ 0 \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} L H S ≥ = ≥ j j − 1 ⋅ g i − 1 , j − j + 1 j ⋅ g i − 1 , j + j + 1 1 ⋅ a i j ( j + 1 ) j ⋅ a i − g i − 1 , j 0
所以结论得证。
然后就可以从前往后枚举 a i a_i a i ,维护 g g g 数组,每次相当于是在某个位置插入和将某个后缀区间加某个数,并且需要快速找到分界点,可以用平衡树维护。
时间复杂度:O ( n log n ) O(n\log n) O ( n log n ) 。
Code1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include <bits/stdc++.h> 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) { 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 ; while (T--) dickdreamer (); return 0 ; }