https://www.luogu.com.cn/problem/P2900
可以想到,我们只关注对答案有贡献的物品,那么对于一个物品的w和l都<=另一个,那么这个物品可以完全合并到另一个物品的组里面去,他不用背考虑。
于是我们按w从小到大第一关键字,l从大到小第二关键字,每个相同的w只取最大的,然后单调栈搞出个w从小到大,l从大到小的序列。
我们知道肯定是w越小的配l越大的比较好,所以肯定是这样顺序下连续的一段一段一组更优,接下来我们考虑如何将他们合并
dp[i]=min(w[i]*l[k]+dp[k-1]),表示前i个合并的最小值。
这个式子可以看出,对于每个i,w[i]恒定,l[k],dp[k-1]是之前求出来的一些点,要求dp[i]最小,很显然就是固定斜率,之前有很多点,斜率优化。
dp[k-1]=w[i]*(-l[k])+dp[i],我们以(-l[k])为横坐标,dp[k-1]为纵坐标,那么与y轴的焦点的值就是dp[i],我们希望dp[i]尽可能小。
(-l[k])递增,dp[k-1]递增,希望与y轴截距最小,通过画图分析,我们应该用单调队列维护一个斜率递增的序列.
#include<bits/stdc++.h>
using namespace std;
const int maxl=5e4+10;
int n,cnt,top;
long long ans;
long long dp[maxl];
struct node
{
long long x,y;
}a[maxl],b[maxl],s[maxl];
__int128 t=1;
inline bool cmp(const node &a,const node &b)
{
if(a.x==b.x)
return a.y>b.y;
return a.x<b.x;
}
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&a[i].x,&a[i].y);
sort(a+1,a+1+n,cmp);
int top=0;
for(int i=1;i<=n;i++)
{
if(top>0 && a[i].x==s[top].x)
continue;
while(top>0 && s[top].y<=a[i].y)
top--;
s[++top]=a[i];
}
cnt=top;
for(int i=1;i<=top;i++)
b[i]=s[i];
}
inline long long calc(long long k,int id)
{
return -k*s[id].x+s[id].y;
}
inline bool cmpk(node a,node b,node c)
{
return t*(c.y-b.y)*(b.x-a.x)<=t*(b.y-a.y)*(c.x-b.x);
}
inline void mainwork()
{
ans=0;int head=1,tail=0;node d;
for(int i=1;i<=cnt;i++)
{
d=node{-b[i].y,dp[i-1]};
while(head<tail && cmpk(s[tail-1],s[tail],d))
tail--;
s[++tail]=d;
while(head<tail && calc(b[i].x,head)>=calc(b[i].x,head+1))
head++;
dp[i]=calc(b[i].x,head);
}
}
inline void print()
{
printf("%lld",dp[cnt]);
}
int main()
{
prework();
mainwork();
print();
return 0;
}