题目
n(n<=2e5)个人,m(m<=2e5)个物品,
第i个人的动力是bi(0<=bi<=1e9),第j个物品的价值是cj(0<=cj<=1e9)
Takahashi可以给每个物品定价,其中第j个物品定价pj,
当且仅当bi+cj>=pj时,第i个人会买第j个物品
现在要求商品收益最大化(即最大化买第j个物品的人*pj的总和),
对于每个物品j,输出最大化第j件物品的收益
思路来源
虽然自己搞过去了,但还是总结一下
题解
//(a1+b1)*5 (a2+b1)*4 (a3+b1)*3 (a4+b1)*2 (a5+b1)*1
//(a1+b2)*5 (a2+b2)*4 (a3+b2)*3 (a4+b2)*2 (a5+b2)*1
//c1+b1*5 c2+b1*4 c3+b1*3 c4+b1*2 c5+b1
//2+x*5 6+x*4 8+x*3 2+x*2 4+x
//2+x*5<6+x*4 x<4
//2+x*5<8+x*3 2x<6 x<3
观察这个样例,对于第一个物品来说,
定价x=100+120时,收益5*(100+120)
定价x=200+120时,收益4*(200+120)
...
定价x=b[从大到小第i个值]+c[1]时,收益y=i*(b[i]+c[1])=i*c[1]+i*b[i]
收益可以被视为是y=kx+b的函数,其中k=i,b=i*b[i],x即为c[1]
于是,相当于有n条直线,其中变量是cj,可以视为x
根据x的值从小到大变化,收益y的最优直线,可能是n条中的某一条直线最优
所以,求y值最大的最优直线即可,构建凸包或者在李超树上根据x查询
此处是离线做法,先构建凸包,然后将cj值离线排增序,不断在凸包往前扫,构建答案
因为值域1e9,所以李超树的做法需要动态开点,是一个板子题,
可以参考从zhoukangyang那里借鉴的板子:
动态开点李超树(例题+板子总结)_Code92007的博客-CSDN博客
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const db eps=1e-8;
const int N=2e5+10,M=2e5+10;
struct node{
int c,id;
}f[N];
bool operator<(node a,node b){
return a.c<b.c;
}
struct line{
ll k,b;
ll cal(int x){
//printf("k:%lld v:%lld b:%lld ans:%lld\n",k,v,b,1ll*k*v+b);
return 1ll*k*x+b;
}
}l[N],stk[N];
bool cmp(line a,line b){
return a.k<b.k||(a.k==b.k && a.b<b.b);
}
db crossx(line a,line b){
return (b.b-a.b)/(a.k-b.k);
}
int n,m,c,b[N];
ll ans[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&b[i]);
}
sort(b+1,b+n+1,greater<int>());
for(int i=1;i<=n;++i){
l[i].k=i;
l[i].b=1ll*i*b[i];
//printf("k:%lld b:%lld\n",l[i].k,l[i].b);
}
for(int i=1;i<=n;++i){
while(c){
if(stk[c].k==l[i].k)c--;
else if(c>1 && 1ll*(l[i].b-stk[c].b)*(stk[c-1].k-stk[c].k)<=1ll*(stk[c].b-stk[c-1].b)*(stk[c].k-l[i].k))c--;
else break;
}
stk[++c]=l[i];
}
for(int i=1;i<=m;++i){
scanf("%d",&f[i].c);
f[i].id=i;
}
sort(f+1,f+m+1);
int p=1;
for(int i=1;i<=m;++i){
//printf("i:%d c:%d cal:%lld\n",i,f[i].c,stk[p].cal(f[i].c));
while(p+1<=c && stk[p].cal(f[i].c)<=stk[p+1].cal(f[i].c))p++;
//printf("i:%d p:%d k:%lld b:%lld\n",i,p,stk[p].k,stk[p].b);
ans[f[i].id]=stk[p].cal(f[i].c);
}
for(int i=1;i<=m;++i){
printf("%lld%c",ans[i]," \n"[i==m]);
}
return 0;
}
//(a1+b1)*5 (a2+b1)*4 (a3+b1)*3 (a4+b1)*2 (a5+b1)*1
//(a1+b2)*5 (a2+b2)*4 (a3+b2)*3 (a4+b2)*2 (a5+b2)*1
//c1+b1*5 c2+b1*4 c3+b1*3 c4+b1*2 c5+b1
//2+x*5 6+x*4 8+x*3 2+x*2 4+x
//2+x*5<6+x*4 x<4
//2+x*5<8+x*3 2x<6 x<3