【JZOJ5390】逗气

Description

这里写图片描述

Solution

这题首先把绝对值拆掉,发现它是这样一个东西: bjajdi+cidi(ajci) ,我们发现 cidi 对我们求解没有影响,先去掉,然后就是这个东西 ajdi+bj ,于是对于每一个 aj,bj ,可以当作一个一次函数 f(x)=ajx+bj ,那么这里的 di 就作为自变量 x 。现在对于一个di要求最大值,那么也就是求当前所加入的直线与 x=di 这条与y轴平行线的交点的最高点。

那么我们有经典做法线段树:区间 [l,r] 记录在此区间内往上露出最长长度的线段(你可以理解为一束光在这个区间内从上往下照,照到长度最多的线段)(对于这题是直线),那么更新就维护区间的性质即可(如果没有则直接覆盖,有则选择向上露出长度多的那条,剩下的那条往下更新区间)。查询也很容易,就从根一直往下找这个位置取最大值就可以了。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 200010
#define inf 10000000000000000ll
#define ll long long
using namespace std;
struct node{
    int x,y;
    int wz;
}w1[N],w2[N];
bool cmp(node x,node y){
    return x.x<y.x;
}
struct tree{
    int l,r,o;
}tr[N];
struct line{
    int k,b;
    ll f(int x){
        return (ll)k*x+b;
    }
}ln[N];
int cc=1,tot=0;
void pdl(int v){
    if(!tr[v].l) tr[v].l=++cc;
}
void pdr(int v){
    if(!tr[v].r) tr[v].r=++cc;
}
double meet(int x,int y){
    return (ln[y].b-ln[x].b)*1.0/(ln[x].k-ln[y].k);
}
void change(int v,int l,int r,int x)
{
    if(!tr[v].o) {tr[v].o=x;return;}
    int o=tr[v].o;
    double p=meet(x,o);
    int mid=(l+r)/2;
    if(p<=l*1.0 || p>=r*1.0 || ln[x].k==ln[o].k)
    {
        if(ln[x].f(mid)>ln[o].f(mid)) tr[v].o=x;
        return;
    }
    if(p<=mid*1.0)
    {
        pdl(v);
        if(ln[x].k>ln[o].k) tr[v].o=x,change(tr[v].l,l,mid,o);
        else change(tr[v].l,l,mid,x);
    }
    else
    {
        pdr(v);
        if(ln[x].k<ln[o].k) tr[v].o=x,change(tr[v].r,mid+1,r,o);
        else change(tr[v].r,mid+1,r,x);
    }
}
ll find(int v,int l,int r,int x)
{
    if(!v) return -inf;
    int o=tr[v].o;
    if(!o) return -inf;
    ll tmp=ln[o].f(x);
    if(l==r) return tmp;
    int mid=(l+r)/2;
    if(x<=mid) return max(tmp,find(tr[v].l,l,mid,x));
    else return max(tmp,find(tr[v].r,mid+1,r,x));
}
int lf=1;
void add(int x){
    ln[++tot].k=lf*w1[x].x,ln[tot].b=w1[x].y;
}
ll z[N];
int main()
{
    freopen("gas.in","r",stdin);
    freopen("gas.out","w",stdout);
    int n,m;
    scanf("%d %d",&n,&m);
    int mx=0;
    fo(i,1,n) scanf("%d %d",&w1[i].x,&w1[i].y),mx=max(mx,max(w1[i].x,w1[i].y));
    fo(i,1,m) scanf("%d %d",&w2[i].x,&w2[i].y),w2[i].wz=i,mx=max(mx,max(w2[i].x,w2[i].y));
    sort(w1+1,w1+n+1,cmp),sort(w2+1,w2+m+1,cmp);
    int p=0;
    fo(i,1,m)
    {
        while(w1[p+1].x<=w2[i].x && p<n) p++,add(p),change(1,1,mx,tot);
        int wz=w2[i].wz;
        z[wz]=max(z[wz],find(1,1,mx,w2[i].y)-(ll)w2[i].x*w2[i].y);
    }
    lf=-1,p=n+1;
    memset(tr,0,sizeof(tr)),tot=0,cc=1;
    fd(i,m,1)
    {
        while(w1[p-1].x>=w2[i].x && p>1) p--,add(p),change(1,1,mx,tot);
        int wz=w2[i].wz;
        z[wz]=max(z[wz],find(1,1,mx,w2[i].y)+(ll)w2[i].x*w2[i].y);
    }
    fo(i,1,m) printf("%lld\n",z[i]);
}
posted @ 2017-10-06 21:56  sadstone  阅读(57)  评论(0编辑  收藏  举报