Description
小B有一个序列A
给多个询问,每次给一个起始点,一个操作步数,他从这个起始点出发。
每轮操作他先选择当前位置的数,然后它有两种选择呆在当前位置或去往当前位置-1的位置。
每次询问他得到的最小值。
Sample Input
6
2 2 3 4 3 4
4
4 5
3 4
3 4
2 3
Sample Output
12
9
9
5
首先得到一个策略肯定是一直往i-1走,然后一直呆在一个位置。
设当前询问为x,y。
得一个答案式:ans=min(s[y]-s[i]+a[i]*(x-y+i))(1<=i<=y)
然后可化成这样:ans=min(s[y]+a[i]*(x-y)-s[i]+i*a[i])(1<=i<=y)
然后你发现a[i]*(x-y)-s[i]+i*a[i]是个直线方程。
因为对于所有i都要加上s[y]于是s[y]的影响可以消去。
那你接下来不就单调栈一下变成一个凸壳,然后二分即可。
这里要注意的是由于斜率并不保证单调递增,那你要想想。
假设对于一个i有j,k两个数可造成贡献,设j < k,a[j]>=a[k]且j~k的数>=a[k]
于是你就可以维护一个斜率单调递增的队列,那就偷税。。。
好久没出来做题,感觉码力极弱。。。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define eps 1e-5
using namespace std;
typedef long long LL;
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
struct node {
int x, y, opt;
} q[510000];
int tp, sta[510000];
double tmp[510000];
int a[510000];
LL gg[510000], s[510000], hh[510000];
bool cmp(node a, node b) {return a.y < b.y;}
bool sl(int i, int j, int k) {
double x = (double)(gg[i] - gg[k]) / (a[k] - a[i]);
double y = (double)x * a[i] + gg[i];
double y2 = (double)x * a[j] + gg[j];
if(y2 - y >= eps) return 1;
else return 0;
}
int main() {
int n = read();
for(int i = 1; i <= n; i++) a[i] = read(), s[i] = s[i - 1] + a[i], gg[i] = (LL)a[i] * i - s[i];
int Q = read();
for(int i = 1; i <= Q; i++) q[i].x = read(), q[i].y = read(), q[i].opt = i;
sort(q + 1, q + Q + 1, cmp);
sta[tp = 1] = 1;
int now = 0;
for(int i = 1; i <= Q; i++) {
while(now < q[i].y) {
now++;
while(tp && a[sta[tp]] >= a[now]) tp--;
while(tp > 1 && sl(sta[tp - 1], sta[tp], now)) tp--;
sta[++tp] = now;
if(tp > 1) tmp[tp - 1] = (double)(gg[sta[tp]] - gg[sta[tp - 1]]) / (a[sta[tp - 1]] - a[sta[tp]]);
} int l = 1, r = tp - 1;
double uu = q[i].x - q[i].y;
int ans = 0;
while(l <= r) {
int mid = (l + r) / 2;
if(tmp[mid] - uu >= eps) l = mid + 1, ans = mid;
else r = mid - 1;
} ans = sta[ans + 1];
hh[q[i].opt] = (LL)a[ans] * ans - s[ans] + s[q[i].y] + a[ans] * uu;
} for(int i = 1; i <= Q; i++) printf("%lld\n", hh[i]);
return 0;
}