CF678F Lena and Queries 题解
对于每一次询问,有 \(x\times q+y=ans\)。
可以推出: \(y=-q\times x+y\),那么不难看出这是一个直线解析式,而 \(x,y\) 则是集合中的点,所以是固定的,斜率 \(-q\) 已给出,要使 \(ans\) 最大,那么截距,即直线与 \(y\) 轴交点越高。
那么可以想到维护凸包,维护凸包方法挺多,平衡树、李超线段树或是 cdq 其实都可以,但由于带增加与删除,所以 cdq 不好处理。看到删除可以选择平衡树,但增加不好维护。看到增加想到李超线段树,但又不好删除。
这个时候就轮到我们主角出场:线段树分治。
线段树上的每个节点代表这个时间存在的节点的集合,那么我们可以先离线预处理出来每个节点的进来和出去的时间,在线段树上进行区间修改。
注意:线段树分治不会进行上传与下放操作,节点间是独立的。
那么每放入一个点,维护上凸包的性质,保证斜率是递减的,如果不是就弹出之前的点。
对于每个询问,找到询问的时间,然后在线段树从上往下找,经过的每个节点都对这个节点中的集合求一次本询问的最佳答案,即斜率比前面小且比后面大的那个点,然后更新所有集合的最大值,求询问可以利用二分来解决。
时间复杂度:\(O(n\times\log_2^2(n))\)。
不够优秀,继续优化。
对于每次询问,按照斜率递减排序,然后利用每个集合的单调性,寻找出第一个最佳答案,即斜率比前面小比后面大的点。下次更新时就可以从这个点开始往下找了。
时间复杂度:\(O(n\times \log(n))\)
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;
const int N=3e5+5;
int n,id[N],cnt,cnt2,nl,nr,k,lent[4*N],ans[N];
int num;
vector<int>f[4*N];
int t[4*N];
struct node
{
int from,to,x,y;
}a[N];
struct ask
{
int name,tim,a;
}q[N];
int cmp(node fi,node se)
{
if(fi.x==se.x)return fi.y<se.y;
return fi.x<se.x;
}
int cmp2(ask fi,ask se)
{
return -fi.a>-se.a;
}
inline int ls(int x)
{
return x<<1;
}
inline int rs(int x)
{
return x<<1|1;
}
double clac(int x1,int y1,int x2,int y2)
{
if(x1<=-1e17||x2<=-1e17)return 1e18;
if(x1==x2)return 1e18;
return 1.0*(y1-y2)/(1.0*(x1-x2));
}
void build(int x,int l,int r)
{
f[x].push_back(0);
if(l==r)return;
int mid=(l+r)>>1;
build(ls(x),l,mid);
build(rs(x),mid+1,r);
}
void update(int x,int l,int r)
{
if(l>=nl&&r<=nr)
{
while(lent[x]>1&&clac(a[f[x][lent[x]]].x,a[f[x][lent[x]]].y,a[f[x][lent[x]-1]].x,a[f[x][lent[x]-1]].y)<=clac(a[k].x,a[k].y,a[f[x][lent[x]]].x,a[f[x][lent[x]]].y))lent[x]--;
int len=f[x].size();
if(len-1==lent[x])f[x].push_back(k),lent[x]++;
else f[x][++lent[x]]=k;
return;
}
int mid=(l+r)>>1;
if(mid>=nl)update(ls(x),l,mid);
if(mid<nr)update(rs(x),mid+1,r);
}
void search(int x,int l,int r)
{
int len=lent[x]+1;
for(;t[x]<len-1;t[x]++)
{
if(clac(a[f[x][t[x]]].x,a[f[x][t[x]]].y,a[f[x][t[x]+1]].x,a[f[x][t[x]+1]].y)<=-1.0*q[k].a)break;
}
if(t[x]!=0)
{
if(q[k].a*a[f[x][t[x]]].x+a[f[x][t[x]]].y>num)
{
num=q[k].a*a[f[x][t[x]]].x+a[f[x][t[x]]].y;
}
}
else if(len>1)num=max(num,q[k].a*a[f[x][1]].x+a[f[x][1]].y);
if(l==r)return;
int mid=(l+r)>>1;
if(mid>=nl)search(ls(x),l,mid);
else search(rs(x),mid+1,r);
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
int opt;
scanf("%lld",&opt);
if(opt==1)
{
int x,y;
scanf("%lld%lld",&x,&y);
a[++cnt]={i,n,x,y};
id[i]=cnt;
}
if(opt==2)
{
int k;
scanf("%lld",&k);
a[id[k]].to=min(a[id[k]].to,i-1);
}
if(opt==3)
{
int x;
scanf("%lld",&x);
q[++cnt2]={cnt2,i,x};
}
}
a[0].x=a[0].y=-1e18;
build(1,1,n);
sort(a+1,a+1+cnt,cmp);
for(int i=1;i<=cnt;i++)
{
nl=a[i].from,nr=a[i].to,k=i;
update(1,1,n);
}
sort(q+1,q+1+cnt2,cmp2);
for(int i=1;i<=cnt2;i++)
{
nl=q[i].tim,k=i;
num=-9e18;
search(1,1,n);
ans[q[i].name]=num;
}
for(int i=1;i<=cnt2;i++)
{
if(ans[i]<=-9e18+5)printf("EMPTY SET\n");
else printf("%lld\n",ans[i]);
}
return 0;
}

浙公网安备 33010602011771号