首先对 (a) 求个前缀最小值,对 (b)求个后缀最小值。
放到二维平面上来开,把 (u) 看成横坐标, (v) 看成纵坐标,想切开的话要在左上区域内选个点做为切割点。
实际上我们只需要考虑组成类似上凸包的点,这些点纵坐标递增。
设 (f[i]) 为考虑了前 (i) 个时的答案,则 (f[i]=f[j]+a[x[j+1]-1]*b[y[i]+1])
画个图应该就很好理解了。
然后斜率优化一下就行了。
#include<algorithm>
#include<iostream>
#include<cstdio>
#define DB double
#define LL long long
using namespace std;
int n, m, top, head, tail;
const int N = 300010;
int q[N];
LL a[N], b[N], f[N];
struct dian
{
int x, y;
friend bool operator <(const dian &a, const dian &b)
{
return a.x == b.x ? a.y < b.y : a.x < b.x;
}
} d[N];
DB Y(int x) {return f[x];}
DB X(int x) {return a[d[x + 1].x - 1];}
DB xl(int x, int y)
{
if (X(x) == X(y))return 9e18;
return (Y(y) - Y(x)) / (X(x) - X(y));
}
signed main()
{
cin >> n >> m;
for (int i = 1; i <= n; ++i)scanf("%lld", &a[i]);
for (int i = 1; i <= n; ++i)scanf("%lld", &b[i]);
for (int i = 1; i <= m; ++i)scanf("%d%d", &d[i].x, &d[i].y);
sort(d + 1, d + 1 + m);
for (int i = 2; i <= n; ++i)a[i] = min(a[i], a[i - 1]);
for (int i = n - 1; i >= 1; --i)b[i] = min(b[i], b[i + 1]);
for (int i = 1; i <= m; ++i)
{
if (top && d[top].x == d[i].x && d[top].y <= d[i].y)--top;
d[++top] = d[i];
}
m = top; top = 0;
for (int i = 1; i <= m; ++i)
if (d[i].y > d[top].y)d[++top] = d[i];
q[head = tail = 1] = 0;
for (int i = 1; i <= top; ++i)
{
while (head < tail && xl(q[head], q[head + 1]) <= 1.0 * b[d[i].y + 1])++head;
f[i] = f[q[head]] + a[d[q[head] + 1].x - 1] * b[d[i].y + 1];
while (head < tail && xl(q[tail - 1], q[tail]) > xl(q[tail], i))--tail;
q[++tail] = i;
}
cout << f[top];
return 0;
}