zkw线段树维护区间凸包,由于凸包的上传需要归并排序,为保证均摊复杂度,一个点只在同一层的右边一个点对应的区间插入完毕时才上传信息,删除时则清除被删除叶子到根的路径的信息,查询则在凸包上二/三分。
#include<bits/stdc++.h> typedef long long i64; const int M=1e5,N=300007,P=998244353,mx=524288; char ib[M+7],*ip=ib+M; int G(){ if(ip==ib+M)fread(ip=ib,1,M,stdin)[ib]=0; return *ip++; } int _(){ int x=0,f=1; if(ip<ib+M-100){ while(*ip<48)*ip++=='-'?f=-1:0; while(*ip>47)x=x*10+*ip++-48; }else{ int c=G(); while(c<48)c=='-'?f=-1:0,c=G(); while(c>47)x=x*10+c-48,c=G(); } return x*f; } struct pos{ int x,y; i64 operator*(const pos&w)const{return i64(x)*w.y-i64(y)*w.x;} pos operator-(const pos&w)const{return (pos){x-w.x,y-w.y};} }ps[20][N],*tr[mx+N],*_a; int cnt[mx+N],now=0,_p; void ins(const pos&p){ if(_p&&_a[_p-1].x==p.x){ if(_a[_p-1].y>=p.y)return; --_p; } while(_p>1&&(_a[_p-2]-_a[_p-1])*(p-_a[_p-1])<=0)--_p; _a[_p++]=p; } void up(int w){ if(cnt[w])return; _a=tr[w];_p=0; pos*ls=tr[w<<1],*le=ls+cnt[w<<1],*rs=tr[w<<1^1],*re=rs+cnt[w<<1^1]; while(ls!=le&&rs!=re)ins(ls->x<rs->x?*ls++:*rs++); while(ls!=le)ins(*ls++); while(rs!=re)ins(*rs++); cnt[w]=_p; } void push(){ int x=_(),y=_(),w=mx+(++now); cnt[w]=1; *tr[w]=(pos){x,y}; for(;w&1;w>>=1,up(w-1)); } void pop(){ for(int w=mx+now-->>1;cnt[w]>0;w>>=1)cnt[w]=0; } void maxs(i64&a,i64 b){if(a<b)a=b;} i64 que(int w,pos p){ if(!cnt[w])return std::max(que(w<<1,p),que(w<<1^1,p)); pos*a=tr[w]; int L=0,R=cnt[w]-1,M; while(R-L>5)(M=L+R>>1,p*a[M]>p*a[M+1])?(R=M):(L=M+1); i64 v=p*a[L++]; while(L<=R)maxs(v,p*a[L++]); return v; } int query(){ i64 ans=-(1ll<<61); int l=_(),r=_(),x=_(),y=_(); pos p=(pos){x,y}; for(l+=mx-1,r+=mx+1;r-l!=1;l>>=1,r>>=1){ if(~l&1)maxs(ans,que(l+1,p)); if(r&1)maxs(ans,que(r-1,p)); } x=ans%P; return x<0?x+P:x; } int main(){ _(); for(int m,sum;sum=now=0,m=_();printf("%u\n",sum)){ for(int l=mx,r=mx+std::min(300005,m),dep=0;l;l>>=1,r>>=1,++dep){ for(int i=l;i<=r;++i)tr[i]=ps[dep]+(i-l<<dep),cnt[i]=0; } for(int w=mx;w;w>>=1)cnt[w]=cnt[w-1]=-1; for(;m;--m){ int o=_(); if(o==1)push(); else if(o==2)pop(); else sum^=query(); } } return 0; }