【JZOJ5390】逗气
Description
Solution
这题首先把绝对值拆掉,发现它是这样一个东西:
bj−ajdi+cidi(aj≤ci)
,我们发现
cidi
对我们求解没有影响,先去掉,然后就是这个东西
−ajdi+bj
,于是对于每一个
aj,bj
,可以当作一个一次函数
f(x)=−ajx+bj
,那么这里的
di
就作为自变量
x
。现在对于一个
那么我们有经典做法线段树:区间 [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]);
}