洛谷2900 [USACO08MAR]土地征用Land Acquisition (斜率优化+dp)

自闭的一批…为什么斜率优化能这么自闭。

首先看到这个题的第一想法一定是按照一个维度进行排序。

那我们不妨直接按照 h i h_i 排序。

我们令 d p [ i ] dp[i] 表示到了第 i i 个矩形的答案是多少。

之后我们会发现,对于 d p [ i ] dp[i] 的转移

d p [ i ] = d p [ j 1 ] + h [ j ] m n [ j ] [ i ] dp[i]=dp[j-1]+h[j]*mn[j][i]

其中 m n [ j ] [ i ] mn[j][i] 表示 j i j到i 的最小值。
qwq我们发现对于一个含有最值的柿子,他没法转移qwq

那我们不妨仔细考虑一下。

对于一个排在 i i 后面的矩阵 j j ,如果他的 w w 小于前缀 w m a x w_{max} ,那么他就可以直接和之前某个矩阵合买了。

那这样就能去掉很多没有用的矩阵
剩下的矩阵就是一个 h h 单调不升, w w 单调不降的序列。

那么这时候
d p [ i ] = m a x ( d p [ j 1 ] + h [ j ] w [ i ] ) dp[i]=max(dp[j-1]+h[j]*w[i])

经过推柿子

d p [ j 1 ] d p [ k 1 ] h [ j ] h [ k ] > w [ i ] \frac{dp[j-1]-dp[k-1]}{h[j]-h[k]} > -w[i]

然后直接斜率优化就可以qwq

这里有两个要注意的地方!!!!!!

首先,我们要比较的是当前的 w w 和前缀 w w 的最大值,而不能比较他的和上一个矩阵(因为上一个矩阵可能也是被完全替代的)。

其次!因为 h [ j ] h [ k ] < 0 h[j]-h[k]<0 所以移项要改变!符号!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 2e5+1e2;
struct Node{
	int h,w;
};
Node a[maxn];
int dp[maxn];
int n;
struct Point{
	int x,y;
};
Point q[maxn];
int chacheng(Point x,Point y)
{
	return x.x*y.y-x.y*y.x;
}
bool count(Point i,Point j,Point k)
{
   Point x,y;
   x.x=k.x-i.x;
   x.y=k.y-i.y;
   y.x=k.x-j.x;
   y.y=k.y-j.y;
   if (chacheng(x,y)>=0) return true;
   return false;
}
int head=1,tail=0;
void push(Point x)
{
	while(tail>=head+1 && count(q[tail-1],q[tail],x)) tail--;
	q[++tail]=x;
}
void pop(int lim)
{
	while (tail>=head+1 && q[head+1].y-q[head].y<lim*(q[head+1].x-q[head].x)) head++;
}
bool cmp(Node a,Node b)
{
	if(a.h==b.h) return a.w>b.w;
	return a.h>b.h;
}
signed main()
{
  n=read();
  for (int i=1;i<=n;i++) a[i].w=read(),a[i].h=read();
  sort(a+1,a+1+n,cmp);
  push((Point){a[1].h,0});
  dp[1]=a[1].w*a[1].h;
  int mx = a[1].w;
  for (int i=2;i<=n;i++)
  {
  	if (a[i].w<=mx) 
  	{
  		dp[i]=dp[i-1];
  		continue;
	}
	mx=max(mx,a[i].w);
	dp[i]=dp[i-1]+a[i].w*a[i].h;
  	pop((-1ll)*a[i].w);
  	Point now = q[head];
  	dp[i]=min(now.y+a[i].w*now.x,dp[i]);
  	push((Point){a[i].h,dp[i-1]});	
  }
  cout<<dp[n];
  return 0;
}

猜你喜欢

转载自blog.csdn.net/y752742355/article/details/85290944