【ZROI317】【18 提高 2】A (乱搞+凸包+决策单调性)

本文介绍了一种通过简化多项式函数来解决特定线性优化问题的方法,并详细解释了如何筛选和处理直线,以找到给定x值时的最大或最小值。此外,还提供了一个高效的实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       这题其实并不怎么难,可是我现场没做出来QAQ,我还是太弱了,ORZ mjh,zyf dalao 们 tql!!!

       很显然,我们可以将aix2+bixaix2+bix除以一个xx,得到aix+bi,这时,要求aix2+bixaix2+bix,只需要在x>0x>0时求出最大的aix+biaix+bi,在x<0x<0时求出最小的aix+biaix+bi,在x=0x=0时输出00即可。

       这就转化成了给定一堆直线,求出在与直线x=?的交点的最高点或最低点。

       那怎么做呢?我们先考虑x>0x>0的情况:首先,对于ai=ajai=aj,如果bibjbi≥bj就不用考虑(aj,bj)(aj,bj)了,这时我们会发现对于不同的aiai,只有唯一的bibi与之对应(即最大值)。然后呢?我们又会发现,对于一个(ai,bi)(ai,bi),如果存在另一个(aj,bj)(aj,bj)使得ajaiaj≥aibjbibj≥bi,那么也不用考虑(ai,bi)(ai,bi)了。最后,我们会发现,剩下来的直线中,对于ai>ajai>aj,一定满足bi<bjbi<bj。我们按aiai从大到小排序,那么bibi递减。(由于数据范围很小,|ai|,|bi|,|x|32323|ai|,|bi|,|x|≤32323,所以直接桶排即可。)

       现在我们又能发现一个性质,在这个顺序下,随着询问xx的递增,带来最优解的直线的下标一定是单调不下降的(这也非常好证明,因为ai是递增的,随着xx的增大,肯定是越右边带来的贡献越可能大),然后我发现这个最优解的位置并不好求,答案的判断并不满足单调性,于是我场上就想到这,GG

       当时一直困扰我的是这种情况:

       会发现,当x>b1b2a2a1时,(a2,b2)(a2,b2)(a1,b1)(a1,b1)更优,当x>b2b3a3a2x>b2−b3a3−a2时,(a3,b3)(a3,b3)(a2,b2)(a2,b2)更优,但是由于b2b3a3a2<b1b2a2a1b2−b3a3−a2<b1−b2a2−a1,所以直接统计答案并不满足任何单调性,那怎么做呢?我们会发现,当x>b1b3a3a1x>b1−b3a3−a1时,(a3,b3)(a3,b3)(a1,b1)(a1,b1)更优,又因为b1b3a3a1<b1b2a2a1b1−b3a3−a1<b1−b2a2−a1,所以(a3,b3)(a3,b3)先于(a2,b2)(a2,b2)变为可能的最优解,又因为前面证到最优解的下标随着xx的增加而不下降,所以(a2,b2)永远不可能成为最优解,可以直接舍去。再仔细观察,我们发现其实这也正是一个求解凸包的过程,所以由此能得出结论,最优解一定在上凸壳上,将xx代入上凸壳上的每条直线得到的是一个单峰函数,可以直接二分或者用单调性扫一遍(其实不用这个结论一样做QWQ)。这样x>0的情况就做完了。x<0x<0直接反着来即可。

       由于使用了桶排序,所以时间复杂度为Θ(n+q+64647)Θ(n+q+64647) ,是线性的(实际上并没有跑多快QAQ)。

       蒟蒻的暂时是全oj最快的代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int base=32323;
inline void read(int &x)
{
    x=0;
    bool f=false;
    char ch=getchar();
    while(!isdigit(ch))f^=(ch=='-'),ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    if(f)x=-x;
}
struct point{
    int x,y;
};
int n,q;
int a[500010],b[500010];
int qa[500010];
bool has[70010];
point p[70010];
int mx[70010];
bool ok[70010];
long long ans[70010];
inline int calc(int x,point t){return t.x*x+t.y;}
inline void getmax(int &x,int y){x=x<y?y:x;}
void work(int coef)
{
    for(int i=-base;i<=base;i++)mx[i+base]=-1e9;
    for(int i=1;i<=n;i++)getmax(mx[(coef>0?a[i]:-a[i])+base],(coef>0?b[i]:-b[i]));
    int tot=0;
    for(int i=-base;i<=base;i++)if(mx[i+base]>-1e9)p[++tot]=(point){i,mx[i+base]};
    int v=-1e9;
    if(coef>0)
    {
        for(int i=tot;i>=1;i--)
        {
            if(v>=p[i].y)ok[i]=false;
            else
            {
                ok[i]=true;
                v=p[i].y;
            }
        }
    }
    else
    {
        for(int i=1;i<=tot;i++)
        {
            if(v>=p[i].y)ok[i]=false;
            else
            {
                ok[i]=true;
                v=p[i].y;
            }
        }
    }
    int cnt=0;
    for(int i=1;i<=tot;i++)if(ok[i])p[++cnt]=p[i];
    int top=1;
    for(int i=2;i<=cnt;i++)
    {
        while(top>1 && 1LL*(p[i].x-p[top].x)*(p[top].y-p[top-1].y)<=1LL*(p[top].x-p[top-1].x)*(p[i].y-p[top].y))top--;
        p[++top]=p[i];
    }
    int pos=1;
    int l=coef>0?1:-base,r=coef>0?base:-1;
    for(int i=l;i<=r;i++)
    {
        if(!has[i+base])continue;
        while(pos<top && calc(i,p[pos+1])>=calc(i,p[pos]))pos++;
        ans[i+base]=1LL*calc(i,p[pos])*i*coef;
    }
}
int main()
{
    read(n);read(q);
    for(int i=1;i<=n;i++)read(a[i]),read(b[i]);
    for(int i=1;i<=q;i++)read(qa[i]),has[qa[i]+base]=true;
    work(1);
    work(-1);
    for(int i=1;i<=q;i++)printf("%lld\n",ans[qa[i]+base]);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值