应用范围
- 对于一些问题我们可以化成
k∗x+y
最值的问题我们可以通过凸包的制造是最优解满足单调性质(当
k
有序是此时我们用到的节点以后一定不会用到)
流程
- 寻找表达式,并判断是取最大还是最小值(虽然是相对的)
- 造凸包(上下凸包)
- 查询答案
凸包的制造和查询
bool delt(P a,P b,P c){
return 1ll*(b.y-a.y)*(c.x-b.x)<=1ll*(c.y-b.y)*(b.x-a.x);
}
/********构造凸包********/
stk[++t]=p[1];stk[++t]=p[2];
for(int i=3;i<=n;++i){
while(t>1&&delt(stk[t-1],stk[t],p[i]))--t;
stk[++t]=p[i];
}
/********查询凸包********/
for(int i=1,l=1;i<=m;++i){
while(l<t&&calc(l+1,Q[i].x)>=calc(l,Q[i].x))++l;
ans[Q[i].y]=calc(l,Q[i].x);
}
- 对于某些问题其x和
k
<script type="math/tex" id="MathJax-Element-6">k</script>并不是有序的我们需要通过一些处理完成
- 当k不是有序的时候,我们可以通过三分转二分的方法来实现
/********查询凸包********/
for(int i=1;i<=m;i++){
scanf("%d",&k);
int l=1,r=t-1,res=t;
while(l<=r){
int mid=l+r>>1;
if(calc(mid,k)>=calc(mid+1,k))r=mid-1,res=mid;
else l=mid+1;
}
printf("%lld\n",calc(res,k));
}
- 当x不是有序的时候就麻烦了,我们需要用CDQ分治
- 对于每一层分治我们造一个凸包,用左边的信息来更新右边的信息
ll get(int k){
int l=1,r=t;
while(l<r){
int mid=l+r>>1;
if(calc(mid,k)<calc(mid+1,k))r=mid;
else l=mid+1;
}return calc(l,k);
}
void CDQ(int l,int r){
if(l==r)return;
int mid=l+r>>1;
CDQ(l,mid);
sort(p+l,p+mid+1);t=0;
/********构造凸包********/
for(int i=l;i<=mid;i++)if(!cmd[p[i].id]){
while(t>1&&Delt(Q[t-1],Q[t],p[i]))--t;
Q[++t]=p[i];
}/********查询凸包********/
for(int i=mid+1;i<=r;++i)if(cmd[p[i].id]==1){
if(!mark[p[i].id]){
ans[p[i].id]=get(k[p[i].id]);
mark[p[i].id]=1;
}else Min(ans[p[i].id],get(k[p[i].id]));
}CDQ(mid+1,r);
}
*奇葩的删除操作
inline void CDQ(int l,int r){
if(l==r)return;
int mid=l+r>>1,i;
CDQ(l,mid);CDQ(mid+1,r);
m=0;
for(i=l;i<=mid;++i)if(!cmd[i]&&del[i]>r)A[++m]=p[i];
init();
for(;i<=r;++i)if(cmd[i]==1)Min(i,get(p[i].x));
m=0;
for(i=mid+1;i<=r;++i)if(cmd[i]==2&&st[i]<l)A[++m]=p[st[i]];
init();
for(i=l;i<=mid;++i)if(cmd[i]==1)Min(i,get(p[i].x));
}