Codeforces edu round11 F(斜率DP)
2016-04-26 19:55:10
【传送门】
题意:给出 n 个数,有正有负,让你求出一个连续字段 [l,r],使得 Sigma((i - l + 1) * a[i]), l<=i<=r 的值最大
思路:设 Si = a1+a2+...ai,Qi = 1*a1+2*a2+...+i*ai,那么 [l,r] 的答案就是 Qr - Ql-1 - (l - 1) * (Sr - Sl-1)。
设 G = Qr - Ql-1 - (l - 1) * (Sr - Sl-1)
= Qr - (l-1) * Sr - Ql-1 + (l-1) * Sl-1
令 y = -Ql-1 + (l-1) * Sl-1 ,x = l-1
则 G = Qr - x * Sr + y
即 y = x * Sr - Qr + G
那么我们枚举 r ,对于每个 r 找到一个 l 使得 G 最大,那么也就是对于斜率为 Sr 且过(x,y)的直线在 y 轴上的截距最大。
考虑 r 时,1~r-1 可以看成二维坐标上的一些点,那么我们维护一个上凸的凸壳,那么把斜率为 Sr 的直线从上方降下来,与直线相交的第一个点就是答案了,由于我们维护的是凸壳,所以从凸壳左边到右边,答案是先递增后递减的,因此可以在凸壳上三分 / 二分,就能得到答案了,凸壳可以用类似凸包的方法来维护。
Notice:注意整数三分的细节。
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <time.h> 5 #include <math.h> 6 #include <vector> 7 #include <map> 8 #include <set> 9 #include <stack> 10 #include <queue> 11 #include <string> 12 #include <iostream> 13 #include <algorithm> 14 using namespace std; 15 16 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 17 #define MEM(a,b) memset(a,b,sizeof(a)) 18 #define MP(a,b) make_pair(a,b) 19 #define PB push_back 20 #define X first 21 #define Y second 22 23 typedef long long ll; 24 typedef pair<ll,ll> pll; 25 const double eps = 1e-8; 26 const int INF = (1 << 30) - 1; 27 const int MAXN = 200010; 28 29 int n,sz; 30 ll ans,S[MAXN],Q[MAXN]; 31 pll sta[MAXN]; 32 33 ll Gety(int p){ 34 return -Q[p - 1] + 1ll * (p - 1) * S[p - 1]; 35 } 36 37 void Insert(ll x,ll y){ 38 while(sz >= 2){ 39 ll cur = (y - sta[sz].Y) * (sta[sz].X - sta[sz - 1].X) 40 - (sta[sz].Y - sta[sz - 1].Y) * (x - sta[sz].X); 41 if(cur >= 0) sz--; 42 else break; 43 } 44 sta[++sz] = MP(x,y); 45 } 46 47 ll Cal(int v,ll k){ 48 return sta[v].Y - k * sta[v].X; 49 } 50 51 ll Solve(ll k){ 52 int l = 1,r = sz; 53 while(l < r - 1){ 54 int mid1 = getmid(l,r); 55 int mid2 = getmid(mid1,r); 56 if(mid1 == mid2) break; 57 ll res1 = Cal(mid1,k); 58 ll res2 = Cal(mid2,k); 59 if(res1 > res2) r = mid2; 60 else l = mid1; 61 } 62 ll res = 0; 63 for(int i = l; i <= r; ++i) res = max(res,Cal(i,k)); 64 return res; 65 } 66 67 int main(){ 68 scanf("%d",&n); 69 for(int i = 1; i <= n; ++i){ 70 int a; 71 scanf("%d",&a); 72 ans = max(ans,(ll)a); 73 S[i] = S[i - 1] + a; 74 Q[i] = Q[i - 1] + 1ll * i * a; 75 } 76 Insert(0,Gety(1)); 77 for(int i = 2; i <= n; ++i){ 78 ll res = Solve(S[i]) + Q[i]; 79 ans = max(ans,res); 80 Insert(i - 1,Gety(i)); 81 } 82 printf("%lld\n",ans); 83 return 0; 84 }