「Bear and Bowling 4」Solution

简述题意

给一个长度为 n n n 的序列 a a a ,选取一个连续子序列 { a x , a x + 1 , … , a x + k − 1 } \{a_x,a_{x+1},\dots ,a_{x+k-1}\} {ax,ax+1,,ax+k1} 使得 ∑ i = 1 k i ⋅ a x + i − 1 \sum_{i=1}^k i\cdot a_{x+i-1} i=1kiax+i1 最大。

  • 1 ≤ n ≤ 2 × 1 0 5 , ∣ a i ∣ ≤ 1 0 7 1\leq n\leq 2\times 10^5,|a_i|\leq 10^7 1n2×105,ai107

思路

对于这种下标乘权值的式子,一般都会进行以下处理:
p r e 1 pre1 pre1 表示 a i a_i ai 的前缀和, p r e 2 pre2 pre2 表示 a i × i a_i \times i ai×i 的前缀和,那么一个子序列 [ i , j ] [i,j] [i,j] 的价值即为:

w ( i , j ) = p r e 2 i − p r e 2 j − 1 − ( j − 1 ) × ( p r e 1 i − p r e 1 j − 1 ) w(i,j) = pre2_i - pre2_{j-1} - (j - 1) \times (pre1_i - pre1_{j-1}) w(i,j)=pre2ipre2j1(j1)×(pre1ipre1j1)

不妨令 d p i dp_i dpi 表示以 i i i 结尾的子序列中的最大价值,显然有:

d p i = max ⁡ { ( j − 1 ) × p r e 1 j − 1 − p r e 2 j − 1 − ( j − 1 ) × p r e 1 i } + p r e 2 i dp_i = \max\{(j-1) \times pre1_{j-1}-pre2_{j-1}-(j-1) \times pre1_i\}+pre2_i dpi=max{(j1)×pre1j1pre2j1(j1)×pre1i}+pre2i

这个式子很显然是可以斜率优化的。按照套路,把与 j j j 无关的归到等式左边, i × j i \times j i×j 形式的看成 k × x k \times x k×x,只与 j j j 有关的就是 x , y x,y x,y
那么可得:
b = d p i − p r e 2 i , k = p r e 1 i , x = j − 1 , y = ( j − 1 ) × p r e 1 j − 1 − p r e 2 j − 1 b = dp_i-pre2_i,k=pre1_i,x=j-1,y=(j-1) \times pre1_{j-1} - pre2_{j-1} b=dpipre2i,k=pre1i,x=j1,y=(j1)×pre1j1pre2j1

j j j 视为坐标为 ( x , y ) (x,y) (x,y) 的点,由于我们要最大化截距 b b b,所以维护上凸包(斜率单减)即可。
对于一般的斜率优化,单调队列即可。但是注意到,此题虽然 x x x 单增,但是 k k k 不一定是单调的( a i a_i ai 有负数),所以可以用二分/平衡树/CDQ/李超线段树解决。

当然肯定挑简单的二分写。

代码

如果你 Wrong answer on test 6 \text{Wrong answer on test 6} Wrong answer on test 6,注意子序列左端不能为 0 0 0
如果你 Wrong answer on test 47 \text{Wrong answer on test 47} Wrong answer on test 47,注意初始答案为 max ⁡ { a 1 , a 2 , a 3 , . . . , a n } \max\{a_1,a_2,a_3,...,a_n\} max{a1,a2,a3,...,an},因为可以只选一个元素。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e6 + 5;
int n , pre1[MAXN] , pre2[MAXN] , a[MAXN] , q[MAXN] , head = 1 , tail , ans;
int w(int l , int r) {return pre2[r] - pre2[l - 1] - (l - 1) * (pre1[r] - pre1[l - 1]);}
int X(int i) {return i - 1;}
int Y(int i) {return (i - 1) * pre1[i - 1] - pre2[i - 1];}
long double Slope(int a , int b) {return (Y(a) - Y(b)) * 1.0 / (X(a) - X(b));}
int Find(int x) {
	if (head > tail) return 0;
	int l = head , r = tail;
	while(l < r) {
		int mid = l + r + 1 >> 1;
		if (Slope(q[mid - 1] , q[mid]) >= pre1[x]) l = mid;
		else r = mid - 1;
	}
	return q[l];
}
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	cin >> n;
	for (int i = 1 ; i <= n ; i ++) {
		cin >> a[i];
		ans = max(ans , a[i]);
		pre1[i] = pre1[i - 1] + a[i] , pre2[i] = pre2[i - 1] + i * a[i];
	}
	for (int i = 1 ; i <= n ; i ++) {
		int j = Find(i);
		if (j) ans = max(ans , w(j , i));
		while(head < tail && Slope(i , q[tail]) >= Slope(q[tail] , q[tail - 1])) tail --;
		q[++ tail] = i;
	}
	cout << ans;
    return 0;
}
posted @ 2024-04-30 19:45  Fracture_Dream  阅读(7)  评论(0)    收藏  举报  来源