题解 P3299 【[SDOI2013]保护出题人】

· · 题解

这并不是一道dp

对于每个i,答案是ans_i=max(\frac{sum_i-sum_{j-1}}{x_i+(i-j)d}) 其中sum_i=\sum_{j=1}^{i}a_j

可以斜率优化,因为答案的表达式可以看成(x_i+i*d,sum_i)(j*d,sum_{j-1})两点的斜率

发现对于每个i,(x_i+i*d,sum_i)是个定点

所以对于点(j*d,sum_{j-1})维护一个下凸包,二分查找凸包中与定点相连斜率最大值

把每一个i的答案累加起来即可

Code:

#include <bits/stdc++.h>
#define maxn 100010
#define int long long
using namespace std;
struct data{
    int x, y;
}stk[maxn];
int n, D, sum[maxn], top;
double Ans;

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

double slope(data x, data y){ return 1.0 * (x.y - y.y) / (x.x - y.x); }

signed main(){
    n = read(), D = read();
    stk[0] = (data){0, 0};
    for (int i = 1; i <= n; ++i){
        int x = read(), y = read(); sum[i] = sum[i - 1] + x;
        data tmp = {i * D, sum[i - 1]};
        while (top && slope(stk[top - 1], stk[top]) > slope(stk[top], tmp)) --top;
        stk[++top] = tmp;
        tmp = (data){y + i * D, sum[i]};
        int l = 1, r = top, ans = 0;
        while (l <= r){
            int mid = (l + r) >> 1;
            if (slope(stk[mid], tmp) > slope(stk[mid - 1], tmp)) ans = mid, l = mid + 1; else r = mid - 1;
        }
        Ans += slope(stk[ans], tmp);
    }
    printf("%.0f\n", Ans);
    return 0;
}