CodeForces 631E Product Sum(斜率優化DP+二分|三分) ★
阿新 • • 發佈:2019-02-16
題意:給出n個數,現在可以移動一個數的位置,現在要使和sigma(ai*i)最大,詢問這個最大和。
思路:將一個數向左移動和向右移動是一樣的,現在考慮向左移動。
先預處理出字首和,將一個數向左移動後,那麼改變數為sum[r-1]-sum[l-1]+a[r]*(r-l),考慮列舉r,那麼和r有關的資料就變成了常量。
現在問題轉化成了求a[r]*l-sum[l-1],注意到這裡l和sum[l-]都是遞增的,所以可以考慮用斜率優化來加速dp,
維護一個下凸曲線,然後對於每一個a[r],二分斜率或者三分截距就可以解決當前位置左移的最大值。
直接三分:
#include <cstdio> #include <iostream> #include <cstring> #include <string> #include <cstdlib> #include <algorithm> #include <cmath> #include <vector> #include <set> #include <list> #include <queue> #include <map> #include <bitset> using namespace std; #define L(i) i<<1 #define R(i) i<<1|1 #define INF 0x3f3f3f3f #define pi acos(-1.0) #define eps 1e-3 #define maxn 200100 #define MOD 1000000007 int n; long long a[maxn],cur; long long total,sum[maxn]; long long solve(int x,int y) { if(x <= y) return total - sum[y] + sum[x] + a[x] * (y - x); return total + sum[x-1] - sum[y-1] + a[x] * (y - x); } int main() { int t; //scanf("%d",&t); while(scanf("%d",&n) != EOF) { sum[0] = 0; for(int i = 1; i <= n; i++) { scanf("%lld",&a[i]); sum[i] = sum[i-1] + a[i]; total += i * a[i]; } long long ans = -0x3f3f3f3f3f3f3f3f; for(int i = 1; i <= n; i++) { int l = 1,r = n; long long cur = -0x3f3f3f3f3f3f3f3f; while(l + 1 <= r) { int mid = l + (r - l) / 3; int mmid = r - (r - l) / 3; long long ans1 = solve(i,mid); long long ans2 = solve(i,mmid); if(ans1 > ans2) { r = mmid - 1; cur = max(cur,ans1); } else { l = mid + 1; cur = max(cur,ans2); } } cur = max(cur,solve(i,l)); ans = max(ans,cur); } printf("%lld\n",ans); } return 0; }
斜率優化:
#include<bits/stdc++.h> #define eps 1e-6 #define LL long long #define pii pair<int, int> #define pb push_back #define mp make_pair //#pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; const int MAXN = 200020; const LL INF = 1e17; int n; int a[MAXN]; LL sumv[MAXN]; int Q[MAXN], h, t; struct Point { LL x, y; Point(LL _x = 0, LL _y = 0) : x(_x), y(_y) {} } p[MAXN]; LL getY(int k, int j) { return p[k].y-p[j].y; } LL getX(int k, int j) { return p[k].x-p[j].x; } bool check1(int k, int j) { return getY(Q[k+1], Q[k]) >= getX(Q[k+1], Q[k])*a[j]; } bool check2(int k, int j) { return getY(Q[k], Q[k+1]) <= getX(Q[k], Q[k+1])*a[j]; } int main() { //freopen("input.txt", "r", stdin); scanf("%d", &n); LL ans = 0; for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); sumv[i] = sumv[i-1] + a[i]; ans += (LL)i * a[i]; } for (int i = 1; i <= n; i++) p[i] = Point(i, sumv[i-1]); LL tmp = ans; t = 0; for (int i = 2; i <= n; i++) { while (t > 1 && getY(i-1, Q[t])*getX(Q[t], Q[t-1]) <= getY(Q[t], Q[t-1])*getX(i-1, Q[t])) t--; Q[++t] = i-1; int l = 1, r = t; while (l < r) { int mid = (l+r) >> 1; if (check1(mid, i)) r = mid; else l = mid + 1; } ans = max(ans, tmp+(LL)a[i]*Q[r]-sumv[Q[r]-1]+sumv[i-1]-(LL)i*a[i]); } t = 0; for (int i = 1; i <= n; i++) p[i] = Point(i, sumv[i]); for (int i = n-1; i > 0; i--) { while (t > 1 && getY(Q[t], i+1)*getX(Q[t-1], Q[t]) >= getY(Q[t-1], Q[t])*getX(Q[t], i+1)) t--; Q[++t] = i+1; int l = 1, r = t; while (l < r) { int mid = (l+r) >> 1; if (check2(mid, i)) r = mid; else l = mid + 1; } ans = max(ans, tmp+(LL)Q[r]*a[i]-(LL)i*a[i]-sumv[Q[r]]+sumv[i]); } printf("%I64d", ans); return 0; }