题目传送门
真的舒服我终于第一次自己推出了斜率方程。。
真开心。
解法:
其实这道题Dp还是挺好想的。
Dp方程大概就是f[j]+i到j所有的土地一组的花费。
其实看到斜率优化的题我一般都想怎么去搞单调性。。
排序。。
先按照长排序一遍。(升降随意,我从小到大排的)
对于每块土地i,j<i
那么i的长肯定大于等于j的长(排了序)
如果i的宽也大于等于j的宽的话,j就没用了。。
因为它跟i一组就完全没贡献啊,所以我们可以把他删掉。
这样就会出现。。
前面的土地长都比后面的小。
前面的土地宽都比后面的大。
单调性出来了!!!
所以从j到i分为一组的花费就是j的宽*i的长。
所以Dp方程就为min(f[i],f[j]+a[j+1].r*a[i].l) (l为长,r为宽)
设j<k 且j对于i更优,有方程:
f[j]+a[j+1].r*a[i].l<f[k]+a[k+1].r*a[i].l
(f[k]-f[j])/(a[j+1].r-a[k+1].l)<a[i].l
然后就按照斜率优化去搞呗。
代码实现:
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
typedef long long ll;
using namespace std;
struct node {
ll l,r;
}a[110000],b[110000];
ll f[110000];
bool cmp(node n1,node n2) {
if(n1.l!=n2.l)
return n1.l<n2.l;
return n1.r<n2.r;
}
double slop(int j,int k) {
return double(f[k]-f[j])/double(b[j+1].r-b[k+1].r);
}
int list[110000],head,tail;
int main() {
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&a[i].l,&a[i].r);
sort(a+1,a+1+n,cmp);
int len=0;
for(int i=1;i<=n;i++) {
while(len>0&&b[len].r<=a[i].r)
len--;
len++;b[len]=a[i]; //b为删除没用状态后的所有土地
}
head=1;tail=1;list[1]=0;
for(int i=1;i<=len;i++) {
while(head<tail&&slop(list[head],list[head+1])<b[i].l)
head++;
int t=list[head];
f[i]=f[t]+b[t+1].r*b[i].l;
while(head<tail&&slop(list[tail-1],list[tail])>slop(list[tail],i))
tail--;
list[++tail]=i;
}
printf("%lld\n",f[len]);
return 0;
}