Luogu P2900 [USACO08MAR]土地征用Land Acquisition

这是一道有技巧的斜率优化题。(不会斜率优化,请戳这里)

ai,biia_i,b_i分别为第i块土地的长、宽

首先,我们按长为第一关键字,宽为第二关键字排序,使得长为非下降序列,当长相等时,宽为非下降序列。因为如果一块土地长,宽都大,就可以把比它长宽都小的土地吃掉,所以我们排序后,再删去一些不需要保留的土地,使得所有土地的宽单调递减。

#define a(i) a[i].a
#define b(i) a[i].b
struct node
{
	ll a,b;
	bool operator <(node c)const{return a==c.a?b<c.b:a<c.a;}//这是重载运算符
}a[N];
//bool cmp(node x,node y){return x.a==y.a?x.b<y.b:x.a<y.a;}
//重载运算符不会打,那就只打上面的一行。

		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%lld%lld",&a(i),&b(i));
		sort(a+1,a+n+1);
		int t=1;
		for(int i=2;i<=n;i++)
		{
			while(t&&b(i)>=b(t))t--;
			a[++t]=a[i];
		}
		n=t;

为什么要这样处理呢? 证明:
i&lt;j&lt;kai&lt;aj&lt;akbi&gt;bj&gt;bki&lt;j&lt;k且a_i&lt;a_j&lt;a_k且b_i&gt;b_j&gt;b_k,我们选择i,kji,k为一块,j为一块的话,费用为akbi+ajbja_k*b_i+a_j*b_j。但是,i,j,kakbi把i,j,k分成一块的费用只需a_k*b_i。
所以,我们排序、删土地后,要取连续的一段。

接着,我们设f[i]f[i]表示对前ii块土地分成若干个连续块的最小费用。
则有:f[i]=min  f[j1]+aibj   (0&lt;j&lt;i)f[i]=min~~{f[j-1]+a_i*b_j}~~~(0&lt;j&lt;i)
把它转化为y=kx+by=kx+b的形式:
f[j1]=(ai)bj+f[i]f[j-1]=(-a_i)*b_j+f[i]
       y     =     k      x  +  b~~~~~~~y~~~~~=~~~~~k~~~~~~x~~+~~b
认真看链接的同学知道用斜率优化的题有以下性质:

1.

斜率(kai)(k,即本题中的-a_i)具有单调性.

2.

横坐标x(x)单调递增

3.

决策点(继承点)有序。
一般来说,当前处理的状态(如此题中的f[i]f[i])前面符号正负最好与取最大值还是最小值有关。
对笔者而言,取最小值时前面符号最好为正,反之最好为负。

但是,上面的bjb_j并不单调递增啊。
没事,我们可把上式变为:f[j1]=ai(bj)+f[i]f[j-1]=a_i*(-b_j)+f[i]
这样,k,xf[i],k,x都单调递增,且满足f[i]前面是正号,就可以用斜率优化了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define a(i) a[i].a
#define b(i) a[i].b
using namespace std;
typedef long long ll;
const int N=5e4+10;
int n,q[N],l,r;
ll f[N];
struct node
{
	ll a,b;
	bool operator <(node c)const{return a==c.a?b<c.b:a<c.a;}
}a[N];
inline ll x(int i){return -b(i+1);}
inline ll y(int i){return f[i];}
inline bool pd(int i,int j,int k)
{
	return (y(j)-y(i))*(x(k)-x(j))<(y(k)-y(j))*(x(j)-x(i));
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld%lld",&a(i),&b(i));
	sort(a+1,a+n+1);
	int t=1;
	for(int i=2;i<=n;i++)
	{
		while(t&&b(i)>=b(t))t--;
		a[++t]=a[i];
	}
	n=t;
	l=r=1;q[1]=0;
	for(int i=1;i<=n;i++)
	{
		while(l<r&&y(q[l+1])-y(q[l])<=a(i)*(x(q[l+1])-x(q[l])))l++;
		int j=q[l];
		f[i]=f[j]+b(j+1)*a(i);
		while(l<r&&!pd(q[r-1],q[r],i))r--;
		q[++r]=i;
	}
	printf("%lld
",f[n]);
	return 0;
}
原文地址:https://www.cnblogs.com/zsyzlzy/p/12373914.html