bzoj1597: 土地购买

bzoj1597土地购买

【题目】

  -点击传送-

【分析】

  刚开始没什么思路,但后开就突然开窍了,就是把每块土地的长和宽看做平面直角坐标系里点的横纵坐标

  如下:

  那么如果你要单独购买一块土地,就是作一个长和宽分别等于这个点的横纵坐标的矩形,其面积就是你要付的费用,我们可以规定矩形的左下角为(0,0),右上角就是这个点。

  如果要购买一组土地,题目里说是长和宽分别取最大值然后乘起来,对应到坐标系里就是选最右边的点的横坐标乘以最上边的点的纵坐标。如下:


  那么问题就变成了使用面积和最小的若干个矩形(左下角都是(0,0))将所有的点都覆盖。如下是一种随便绘制的方案:


  不难发现,如果一个点的横纵坐标都小于另一个点,那么这个点的存在就是没有意义的。因为不管用何种方案把横纵坐标都较大的点覆盖时,这个横纵坐标都较小的点一定被覆盖了。而你的目的是覆盖所有的点,所以这些横纵坐标都较小的点存不存在是不会影响答案的。如下:


  我们把这样的点(a,b)叫做红点:存在另一个点(x,y),满足a<=x且b<=y;

  把这样的点(a,b)叫做蓝点:对于其它所有的点(x,y),满足a>x或b>y

  当我们按照定义把点分类并且把红色的点去掉时,发现剩下的点在一个严格单调下降函数的图像上,如下:


  为了方便研究,使用另一张图,如下:

  根据蓝点的定义,可以证明这些蓝点在一个严格单调下降函数图像上。

  然而这会有没思路了。。。。

  回到开始,我们要用面积最小的矩形把这些点覆盖,先随便找几个点,比如我要买图中的第3个点和第6个点所对应的土地,那么我们把它们用矩形框起来。然后我们发现两个东西:

  第一:如果要把第i和第j个点框起来,那么中间所有的点都一定被框在里面了(因为单调下降)

  第二:框起第i个到第j个点的代价是第i个点的纵坐标乘上第j个点的横坐标(因为单调下降)

  如下:

  如果有两个矩形重叠了,比如两个矩形覆盖的点的编号分别是a~b,c~d,其中a<c<b<d那么由单调性可以证明选择a~c,b~d这种方案一定更优,如果一个矩形完全在另一个矩形内部,那你可以直接舍弃这个较小的矩形。

  综上,在最优解中,所有矩形所覆盖的点构成一些不相交的区间。如下是一种随便绘制的方案:

  用f[i]表示覆盖掉前i个点的最小费用,那么进行划分dp,方程为:

  f[i]=min(f[j]+w(j+1,i)),(j<i)

  其中w(j+1,i)表示覆盖第j+1个点到第i个点的费用

  时间复杂度O(N^2)

【优化】

  用w(i,j)表示覆盖第i~j个点的费用,那么w(i,j)=y[i]*x[j]。

  由单调性有y[i+1]<y[i],x[i+1]>x[i],恒成立

  w(i,j)=y[i]*x[j]

  w(i+1,j+1)=y[i+1]*x[j+1]

  w(i+1,j)=y[i+1]*x[j]

  w(i,j+1)=y[i]*x[j+1]

  作差:

  [ w(i,j)+w(i+1,j+1) ] - [ w(i+1,j)+w(i,j+1) ]

=    y[i]*(x[j]-x[j+1]) + y[i+1]*(x[j+1]-x[j])

=    (y[i]-y[i+1])(x[j+1]-x[j]) <0恒成立

  所以w(i,j)+w(i+1,j+1) < w(i+1,j)+w(i,j+1)恒成立

  所以此dp具有决策单调性

  所以就可以进行二分+栈乱搞了

  复杂度O(NlogN)

【代码】

#include <cstdio>
#include <algorithm>
#define maxn 60000
#define inf 1000000000001
#define ll long long
using namespace std;
ll f[maxn], N, q[maxn], tot, top;
struct point
{
	ll x, y;
	bool operator<(point t)const
	{return y==t.y? x<t.x : y>t.y;}
}p[maxn];
struct interval
{ll l, r, k;}s[maxn];
void init()
{
	ll i, j;
	scanf("%lld",&N);
	for(i=1;i<=N;i++)scanf("%lld%lld",&p[i].x,&p[i].y);
	sort(p+1,p+N+1);
	for(i=1;i<=N;i=j+1)
	{
		for(j=i;p[j].y==p[j+1].y;j++);
		if(!tot or p[q[tot]].x<p[j].x)q[++tot]=j;
	}
}
ll w(ll x, ll y)
{return p[q[x+1]].y*p[q[y]].x;}
ll update(ll x, ll y)
{return x<y?f[x]+w(x,y):(ll)1<<60;}
ll find(interval it, ll x)
{
	ll l=it.l, r=it.r, mid=l+r>>1;
	while(l<r)
	{
		if(update(x,mid)<update(it.k,mid))r=mid;
		else l=mid+1;
		mid=l+r>>1;
	}
	return l;
}
void work()
{
	ll i, j, x, l, r, k;
	s[++top]=(interval){1,tot,0};
	for(i=1,j=1;i<=tot;i++)
	{
		if(s[j].r<i)j++;
		f[i]=update(s[j].k,i);
		while(1)
		{
			l=s[top].l, r=s[top].r, k=s[top].k;
			if(update(i,r)<update(k,r))
			{
				if(update(i,l)<update(k,l)){top--;continue;}
				else {s[top].r=find(s[top],i)-1;break;}
			}
			else break;
		}
		if(s[top].r<tot)s[top+1]=(interval){s[top].r+1,tot,i},top++;
	}
}
int main()
{
	init();
	work();
	printf("%lld\n",f[tot]);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值