洛谷 P6047 丝之割 silksong

首先对 \(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;
}

猜你喜欢

转载自www.cnblogs.com/wljss/p/12600529.html