题解 CF631E 【Product Sum】

吹雪吹雪吹

2019-04-17 20:34:58

Solution

[${\large \color{#ff7daf}{\text{CF631E Product Sum}}}$](http://xc.fubuki.cn/2019/%E6%96%9C%E7%8E%87%E4%BC%98%E5%8C%96/) 题意:整个序列的分数为 $\sum\limits_{i=1}^{n}a_i·i$ ,你可以将某个元素向任意方向移动任意位,求一次该操作后可获得的最大分数。 设 $sum_i = \sum\limits_{j=1}^{i}a_i$ 接下来以将第 $i$ 个元素向前移动**到**第 $j$ 个位置为例: 将 $a_i$ 移到 $j$ 增加的分数: $(j-i)·a_i-(sum_j-sum_i)$ 尝试斜率优化,设 $k > j$ 且选择 $k$ 要优于选择 $j$: $(j-i)·a_i-(sum_j-sum_i)<(k-i)·a_i-(sum_k-sum_i)$ 移项后合并同类项: $j·a_i-sum_j<k·a_i-sum_k$ 再移项: $sum_k-sum_j<(k-j)·a_i$ $\frac{sum_j-sum_k}{j-k}<a_i$ 维护上凸壳,二分进行决策即可。 将某一元素向后移动类似,此处不做赘述。 代码(不可AC,但是正确(^_^)): ```cpp #include <cstdio> #include <cstring> #include <algorithm> #define maxn 200005 #define K(x, y) ((1.0 * w[x] - w[y]) / (1.0 * (x) - (y))) #define Calc1(x, y) (sum[(x) - 1] - sum[(y) - 1] - a[x] * ((x) - (y))) #define Calc2(x, y) (a[x] * ((y) - (x)) - (sum[(y)] - sum[(x)])) using namespace std; typedef int LL; int n, stk[maxn], top = 0; LL a[maxn], sum[maxn], ans = 0, w[maxn], res = 0; inline int read() { char ch = getchar(); int ret = 0, f = 1; while (ch > '9' || ch < '0') { if (ch == '-') f = -f; ch = getchar(); } while (ch >= '0' && ch <= '9') ret = ret * 10 + ch - '0', ch = getchar(); return ret * f; } int main() { // freopen("test.in", "r", stdin); // freopen("test.out", "w", stdout); n = read(); for (int i = 1; i <= n; ++i) { a[i] = read(); sum[i] = sum[i - 1] + a[i]; ans += 1ll * i * a[i]; } stk[1] = 1, stk[2] = 2, top = 2; w[1] = sum[0], w[2] = sum[1]; res = max(0ll, Calc1(2, 1)); for (int i = 3; i <= n; ++i) { int l = 1, r = top - 1, j = stk[top]; while (l <= r) { int mid = (r - l >> 1) + l; if (K(stk[mid + 1], stk[mid]) > a[i]) r = mid - 1, j = stk[mid]; else l = mid + 1; } res = max(res, Calc1(i, j)); w[i] = sum[i - 1]; while (top > 1) { if (K(i, stk[top]) > K(stk[top], stk[top - 1])) break; --top; } stk[++top] = i; } stk[1] = n, stk[2] = n - 1, top = 2; w[n] = sum[n], w[n - 1] = sum[n - 1]; res = max(res, Calc2(n - 1, n)); for (int i = n - 3; i > 0; --i) { int l = 1, r = top - 1, j = stk[top]; while (l <= r) { int mid = (r - l >> 1) + l; if (K(stk[mid + 1], stk[mid]) < a[i]) r = mid - 1, j = stk[mid]; else l = mid + 1; } res = max(res, Calc2(i, j)); w[i] = sum[i]; while (top > 1) { if (K(i, stk[top]) < K(stk[top], stk[top - 1])) break; --top; } stk[++top] = i; } printf("%I64d\n", ans + res); return 0; } ``` 多一句嘴,网上三分什么的显然是错的,有兴趣的可以去叉一下。$\color{white}{\text{上面的代码开下 long long 就能过了}}$