BZOJ 1597 [Usaco2008 Mar]土地购买 (斜率优化dp)
题意:
给你一些x*y的土地,分成一组一组购买土地,其代价为这组里的max{x}*max{y},问你全部买下最少多少钱
思路:
有一个土地为x1*y1,如果存在一个x2*y2,且x1<=x2&&y1<=y2,那么我们就不需要考虑x1*y1的土地了
这样我们可以处理出一个x单调递增,同时y单调递减的数组
显然可以看出,每一组购买的土地应该是这个数组中连续的一段
简单证明:在购买的一组土地总量不变的情况下,该组购买方案为一个单独的x1*y1再加上数组中一段连续的土地[l,r],那么max{x}=a[r].x,而max{y}=y1
此时一定存在连续的[l-1,r],其max{y}=a[l-1].y<y1,比选择x1*y1更优,所以应该选连续的一段
所以我们根据新数组,可以写出dp方程:
f[i]=min(f[j]+a[i].x*a[j+1].y)
斜率优化即可
由于a[j+1].y单调递增,我们只需要单调队列维护一个下凸壳
代码:
#include<iostream> #include<cstdio> #include<algorithm> //#include<cmath> #include<cstring> #include<string> #include<stack> #include<queue> #include<deque> #include<set> #include<vector> #include<map> #define fst first #define sc second #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define lson l,mid,root<<1 #define rson mid+1,r,root<<1|1 #define lc root<<1 #define rc root<<1|1 using namespace std; typedef double db; typedef long double ldb; typedef long long ll; typedef unsigned long long ull; typedef pair<int ,int> PI; typedef pair<ll,ll> PLL; const db eps = 1e-6; const int mod = 998244353; const int maxn = 2e6+100; const int maxm = 2e6+100; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; //const db pi = acos(-1.0); int n; PLL a[maxn],b[maxn]; ll f[maxn]; int q[maxn]; bool cmp(PLL a, PLL b){return a.fst==b.fst?a.sc<b.sc:a.fst<b.fst;} int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++){ scanf("%lld%lld", &a[i].fst, &a[i].sc); } sort(a+1,a+1+n,cmp); int tot=0; for(int i = 1; i <= n; i++){ while(tot&&a[i].sc>=b[tot].sc)tot--; b[++tot]=a[i]; } int l=0,r=0; for(int i = 1; i <= tot; i++){ while(l<r&&(f[q[l+1]]-f[q[l]])<=b[i].fst*(b[q[l]+1].sc-b[q[l+1]+1].sc))l++; int j = q[l]; f[i]=f[j]+b[i].fst*b[j+1].sc; while(l<r&&(f[q[r]]-f[q[r-1]])*(b[q[r]+1].sc-b[i+1].sc)>=(f[i]-f[q[r]])*(b[q[r-1]+1].sc-b[q[r]+1].sc))r--; q[++r]=i; } printf("%lld",f[tot]); return 0; } /* 4 100 1 15 150 20 5 1 100 */