• P2900 [USACO08MAR]Land Acquisition G


    https://www.luogu.com.cn/problem/P2900
    (n) 块地,给出每块的长和宽
    每次可以购买一个或多个地,此次购买的代价是这些地中最大的宽乘以最大的长
    问最小花费多少代价能把所有地都买到

    斜率优化dp

    首先想到,如果有一个地比另一个地的长和宽都要小,显然可以不考虑这块地了
    忽略这种地的过程,可以按 (l) 为第一关键字,(w) 为第二来升序排个序,然后用栈维护一下就好
    是这样一个过程:

    std::sort(a+1,a+1+n,cmp);
    for(reg int i=1;i<=n;i++){
    	while(top&&b[top].w<=a[i].w&&b[top].l<=a[i].l) top--;
    	b[++top]=(data){a[i].w,a[i].l};
    }
    

    这样处理完以后(按照在 b 数组里的顺序来看),(l) 是单调不减的
    所以 (w) 应该是单调减,因为如果它不变或者递增的话,就会被当作不用处理的地来忽略掉

    然后一次购买的多块地,在这种排序方式下,一定是连续的
    假设购买了 (x,x+2) 两块地,那么花费是 (w_x l_{x+2}),然后又因为 (w_{x+1}<w_x,l_{x+1}le l_{x+2})
    所以完全可以在不增加任何代价的情况下将第 (x+1) 块地包括进去

    那么可以写出dp的转移方程了 (f_i=min{f_j+w_{j+1} l_i})
    但这样是 (O(n^2)) 的,还需要个斜率优化
    首先可以确定 (b=f_i),比较习惯最小化截距,不过题解区里好像还有最小化 (y) 的?
    然后 (w_{j+1} l_i) 肯定是解析式里的 (kx)
    (k) 应该和 (i) 有关,所以 (k=l_i,x=-w_{j+1})
    那么 (y) 就是 (f_j)

    所以:(f_j=l_i(-w_{j+1})+f_i)
    因为 (l) 递增,所以符合 (k) 递增的条件,可以不用二分
    (w) 递减,所以 (-w) 递增,也符合 (x) 递增的要求

    似乎就可以了,放上代码

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    #define N 50005
    struct data{
    	LL w,l;
    }a[N],b[N];
    int top,n;
    int tail,head;
    int q[N];
    LL f[N];
    inline int cmp(data a,data b){
    	return a.l==b.l?a.w<b.w:a.l<b.l;
    }
    inline LL get_x(int i){return -b[i+1].w;}
    inline LL get_y(int i){return f[i];}
    int main(){
    	n=read();
    	for(reg int i=1;i<=n;i++) a[i].w=read(),a[i].l=read();
    	std::sort(a+1,a+1+n,cmp);
    	for(reg int i=1;i<=n;i++){
    		while(top&&b[top].w<=a[i].w&&b[top].l<=a[i].l) top--;
    		b[++top]=(data){a[i].w,a[i].l};
    	}
    	n=top;
    	tail=head=0;q[0]=0;
    	for(reg int i=1;i<=n;i++){//k=l[i]
    		while(tail<head&&
    		get_y(q[tail+1])-get_y(q[tail])<=b[i].l*(get_x(q[tail+1])-get_x(q[tail]))) tail++;
    		int j=q[tail];
    		f[i]=f[j]+b[j+1].w*b[i].l;
    		while(tail<head&&(get_y(q[head])-get_y(q[head-1]))*(get_x(i)-get_x(q[head]))
    		>=(get_y(i)-get_y(q[head]))*(get_x(q[head])-get_x(q[head-1]))) head--;
    		q[++head]=i;
    	}
    	std::printf("%lld",f[n]);
    	return 0;
    }
    
  • 相关阅读:
    环境装好,开始学习
    懒惰了
    我的net试验田
    时间不够用
    【转帖】关于委托的精彩解说(非常形象)
    【转帖】 CLR 全面透彻解析:托管和本机代码互操作性
    【转贴】C#中的API32
    【转帖】Windows Mobile 开发系列文章收藏 Windows Mobile 6.x
    弹跳圣经——扣篮梦
    【转帖】.Net中C#的DllImport的用法
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13155273.html
Copyright © 2020-2023  润新知