【JZOJ 5390】【NOIP2017提高A组模拟9.26】逗气

Description

这里写图片描述

Solution

把式子的绝对值去掉就变成了一道维护直线的题面了,
这个直接用线段树即可,

像主席树那样,不下传标记,每个点都表示这一条线段,表示线段在这个区间可能会是某些坐标的最优值,
每次加入一条线段,就判断一下这条线是否在这个区间内都是由于原来的线段,否则看看是那一边比原来的优就往哪边走(可能两边都走),

询问只是一个点,所以只要看一下访问路径上的所有直线计算最大值即可,

复杂度 O(nlog(n))

Code

#include <cstdio>
#include <algorithm>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
#define SU(k,b,x) ((LL)(k)*(LL)(x)+(LL)(b))
#define mxSU(k,b,l,r) (max(SU(k,b,l),SU(k,b,r)))
#define miSU(k,b,l,r) (min(SU(k,b,l),SU(k,b,r)))
using namespace std;
typedef long long LL;
const int N=200500,INF=-1e9;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n;
LL Ans[N];
struct qwqw
{
    int k,b,i;
}a1[N],a[N];
bool PX(qwqw q,qwqw w){return q.k<w.k;}
struct qqww
{
    int l,r,k,b;
}b[N*4];
int root,b0;
void change(int l,int r,int &e,int lk,int lb)
{
    if(!e)b[e=++b0]=b[0];
    LL tl=SU(b[e].k,b[e].b,l),tr=SU(b[e].k,b[e].b,r);
    LL ll=SU(lk,lb,l),lr=SU(lk,lb,r);
    if(tl>=ll&&tr>=lr)return;
    if(tl<=ll&&tr<=lr)
    {
        b[e].k=lk,b[e].b=lb;
        return;
    }
    int t=(l+r)>>1;
    if(tl<ll||(SU(b[e].k,b[e].b,t)<SU(lk,lb,t)))change(l,t,b[e].l,lk,lb);
    if((tr<lr)||(SU(b[e].k,b[e].b,t+1)<SU(lk,lb,t+1)))change(t+1,r,b[e].r,lk,lb);
}
LL find(int l,int r,int e,int l1)
{
    if(b[e].k==INF&&b[e].b==INF)return -1e18;
    if(l==r)return SU(b[e].k,b[e].b,l);
    int t=(l+r)>>1;
    LL ans;
    if(l1<=t)ans=find(l,t,b[e].l,l1);
    else ans=find(t+1,r,b[e].r,l1);
    ans=max(ans,SU(b[e].k,b[e].b,l1));
    return ans;
}
int main()
{
    freopen("gas.in","r",stdin);
    freopen("gas.out","w",stdout);
    int q,w,mx=0;
    read(m),read(n);
    fo(i,1,m)read(a1[i].k),read(a1[i].b);
    fo(i,1,n)read(a[i].k),read(a[i].b),mx=max(mx,a[i].b),a[i].i=i;
    sort(a1+1,a1+1+m,PX);
    sort(a+1,a+1+n,PX);
    b[0].k=b[0].b=INF;
    a[n+1].k=a1[m+1].k=2e9;
    root=b0=0;
    for(int i=1,j=1;i<=n||j<=m;)
    {
        if(a[i].k<a1[j].k)
        {
            Ans[a[i].i]=find(1,mx,root,a[i].b)-(LL)a[i].k*a[i].b;
            i++;
        }else
        {
            change(1,mx,root,a1[j].k,a1[j].b);
            j++;
        }
    }
    a[0].k=a1[0].k=-2e9;
    root=b0=0;
    for(int i=n,j=m;i||j;)
    {
        if(a[i].k>a1[j].k)
        {
            LL t=find(1,mx,root,a[i].b)+(LL)a[i].k*a[i].b;
            Ans[a[i].i]=max(t,Ans[a[i].i]);
            i--;
        }else
        {
            change(1,mx,root,-a1[j].k,a1[j].b);
            j--;
        }
    }
    fo(i,1,n)printf("%lld\n",max(0,Ans[i]));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值