比赛的时候因为卡内存,在抠内存的时候改错了,导致赛内没有AC,赛后发现数组开的很小都可以AC。
分析题意我们发现,这题需要求出所有存在的直线形成的上凸壳,那么查询$[L,R]$时在凸壳上二分导数,找到最大值即可。
因为有删除操作,故离线求出每条直线存在的时间区间,在时间线段树上打标记,那么这样会转化成$O(nlog n)$次插入。
那么现在需要维护一个数据结构,支持插入直线,询问单点最值,这显然可以使用李超线段树。
沿着时间线段树进行dfs,用一个栈按时间记录所有修改,那么可以很方便地实现李超线段树的还原。
时间复杂度$O(nlog^2n)$。
#include<cstdio> #include<algorithm> #include<stack> #include<map> using namespace std; typedef pair<int,int>P; typedef long long ll; const int N=100010,M=1500000,E=200010; const int eps=5; const int inf=1000000000; const ll offset=2000000000LL; const ll infll=1LL<<62; int Case,n,m,i,op,x,y; int st[N],en[N],cur; int cq,que[N][2],g[N],nxt[N]; ll ans[N]; int G[131111],V[M],NXT[M],ED; map<P,stack<int> >idx; int root,tot,l[E],r[E],val[E]; int pool[E][2],cpool; struct LINE{ int k,b; }a[N]; inline ll cal(int x,int p){return 1LL*a[x].k*p+a[x].b;} inline void addedge(int x,int y){ nxt[y]=g[x];g[x]=y; } void build(int x,int a,int b){ G[x]=0; if(a==b)return; int mid=(a+b)>>1; build(x<<1,a,mid); build(x<<1|1,mid+1,b); } void tag(int x,int a,int b,int c,int d,int p){ if(c<=a&&b<=d){ V[++ED]=p; NXT[ED]=G[x]; G[x]=ED; return; } int mid=(a+b)>>1; if(c<=mid)tag(x<<1,a,mid,c,d,p); if(d>mid)tag(x<<1|1,mid+1,b,c,d,p); } void ins(int&x,int a,int b,int p,int f){ if(!val[x]){ x=++tot; l[x]=r[x]=0; pool[++cpool][0]=x; pool[cpool][1]=-f; val[x]=p; return; } if(cal(p,a)<cal(val[x],a)&&cal(p,b)<cal(val[x],b)){ pool[++cpool][0]=x; pool[cpool][1]=val[x]; val[x]=p; return; } if(cal(p,a)>=cal(val[x],a)&&cal(p,b)>=cal(val[x],b)){ return; } if(a==b)return; ll mid=((offset+a+b)>>1)-inf; ins(l[x],a,mid,p,x); ins(r[x],mid+1,b,p,x); } inline void umin(ll&a,ll b){if(a>b)a=b;} inline void umax(ll&a,ll b){if(a<b)a=b;} ll TMP; void ask(int x,int a,int b,int c){ if(!val[x])return; umin(TMP,cal(val[x],c)); if(a==b)return; ll mid=((offset+a+b)>>1)-inf; if(c<=mid)ask(l[x],a,mid,c);else ask(r[x],mid+1,b,c); } inline void retrace(int pos){ while(cpool>pos){ int x=pool[cpool][0],y=pool[cpool--][1]; val[x]=y; if(y<=0){ tot--; if(y<0){ if(l[-y]==x)l[-y]=0; if(r[-y]==x)r[-y]=0; } } } } inline ll query(int L,int R){ //find max t that f(t)>f(t-1) int o=L++; while(L<=R){ ll mid=((offset+L+R)>>1)-inf; TMP=infll; ask(root,-inf,inf,mid); ll u=TMP; TMP=infll; ask(root,-inf,inf,mid-1); if(u>TMP)L=(o=mid)+1;else R=mid-1; } TMP=infll; ask(root,-inf,inf,o); return TMP; } void dfs(int x,int a,int b){ int pos=cpool; for(int i=G[x];i;i=NXT[i]){ ins(root,-inf,inf,V[i],0); } if(a==b){ for(int i=g[a];i;i=nxt[i]){ ans[i]=query(que[i][0],que[i][1]); } retrace(pos); return; } int mid=(a+b)>>1; dfs(x<<1,a,mid); dfs(x<<1|1,mid+1,b); retrace(pos); } int main(){ scanf("%d",&Case); while(Case--){ scanf("%d%d",&n,&m); cur=1; for(i=1;i<=n;i++){ scanf("%d%d",&a[i].k,&a[i].b); idx[P(a[i].k,a[i].b)].push(i); st[i]=1; } for(i=1;i<=m;i++){ scanf("%d%d%d",&op,&x,&y); if(op==0){ cq++; que[cq][0]=x; que[cq][1]=y; addedge(cur,cq); } if(op==1){ cur++; a[++n].k=x; a[n].b=y; st[n]=cur; idx[P(x,y)].push(n); } if(op==2){ int z=idx[P(x,y)].top(); idx[P(x,y)].pop(); en[z]=cur++; } } for(i=1;i<=n;i++)if(!en[i])en[i]=cur; build(1,1,cur); for(i=1;i<=n;i++)tag(1,1,cur,st[i],en[i],i); dfs(1,1,cur); for(i=1;i<=cq;i++)printf("%lld ",ans[i]); cq=0; for(i=1;i<=n;i++)en[i]=0; for(i=1;i<=cur;i++)g[i]=0; ED=0; idx.clear(); } }