背景:
晚上还是去了物理竞赛。
好无聊,垃圾卷子。
半路溜了回来(只上了一节课)。
题目传送门:
https://www.luogu.org/problemnew/show/P2900
题意:
有
a
i
∗
b
i
a_i*b_i
ai∗bi的
n
n
n块地,其中,可以任选一些组成一组购买,且最后要买完(可以买多组),每一组的其价值是
x
=
max
{
a
i
}
∗
max
{
b
i
}
x=\max\{ a_i\}*\max\{b_i\}
x=max{ai}∗max{bi}。最后的贡献是
∑
x
\sum x
∑x。求贡献的最小值。
思路:
对于
a
j
≤
a
i
a_j≤a_i
aj≤ai且
b
j
≤
b
i
b_j≤b_i
bj≤bi的土地
j
j
j自然是没有用的,因此可以删掉。可以采用排序然后删除的方式。
具体来说就是按照
a
a
a升序,
b
b
b降序,那么对于相等的
a
a
a,考虑
b
b
b即可。用一个
a
′
,
b
′
a',b'
a′,b′分别存储剩下的有效的土地。
剩下的还是基本套路。
设
f
i
f_i
fi表示购买前
i
i
i块地的最小费用。
则状态转移方程为:
f
i
=
min
j
=
1
i
−
1
f
j
+
a
j
+
1
′
∗
b
i
′
f_i=\min_{j=1}^{i-1}f_j+a'_{j+1}*b_i'
fi=minj=1i−1fj+aj+1′∗bi′。
这里解释一下这个方程,从 j + 1 j+1 j+1到 i i i的区间里( j j j已经被包含在 f j f_j fj里了,因此是从 j + 1 j+1 j+1开始的),这些土地的贡献是 max { a k ′ } ∗ max { b k ′ } , k ∈ [ j + 1 , r ] \max\{a'_k\}*\max\{b'_k\},k∈[j+1,r] max{ak′}∗max{bk′},k∈[j+1,r],又因为我们是对 a a a升序, b b b降序排的,所以我们的 max { a k ′ } = a j + 1 ′ , max { b k ′ } = b i ′ \max\{a'_k\}=a'_{j+1},\max\{b'_k\}=b'_i max{ak′}=aj+1′,max{bk′}=bi′。
同理,得出斜率方程:
f
j
−
f
k
a
k
+
1
′
−
a
j
+
1
′
<
b
i
′
\frac{f_j-f_k}{a'_{k+1}-a'_{j+1}}<b'_i
ak+1′−aj+1′fj−fk<bi′。
同理,在套进去即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
int n,t=0;
int que[100010];
LL f[100010];
struct node{LL x,y;} a[100010],d[100010];
bool cmp(node x,node y)
{
return x.x==y.x?x.y<y.y:x.x>y.x;
}
double calc(int x,int y)
{
return ((double)f[y]-f[x])/((double)a[x+1].x-a[y+1].x);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld %lld",&d[i].x,&d[i].y);
sort(d+1,d+n+1,cmp);
LL ma=0;
for(int i=1;i<=n;i++)
if(d[i].y>ma) ma=d[i].y,a[++t]=d[i];
int head=1,tail=1;
que[1]=0;
for(int i=1;i<=t;i++)
{
while(head<tail&&calc(que[head],que[head+1])<=(double)a[i].y) head++;
f[i]=f[que[head]]+a[que[head]+1].x*a[i].y;
while(head<tail&&calc(que[tail],i)<=calc(que[tail-1],que[tail])) tail--;
que[++tail]=i;
}
printf("%lld",f[t]);
}