查询叉积最大值就是找该向量切凸包上的切点。因为只有在末尾加入删除的操作,我们利用二进制分组,维护
log
log
个凸包,询问的时候在每个凸包上二分找切点即可。
要注意要分别对上下凸壳二分,因为凸包是一个环但二分其实是对一个序列二分,所以直接二分会挂。
程序里还有几个复杂度不对的地方:
1. 二进制分组应该有四个相同的才把前两个合并,而不是有两个相同就合并,否则每次加入/删除第
2k
2
k
个向量复杂度都是
O(2k)=O(n)
O
(
2
k
)
=
O
(
n
)
的,不断操作就挂了。
2. 合并凸包的时候可以用归并排序把复杂度优化到
O(n)
O
(
n
)
。
3. 加上以上优化可以做到
O(nlog2n)
O
(
n
log
2
n
)
,但因为没有要求强制在线,我们可以对于在某个凸包上二分某个切点的问题最后一起离线用旋转卡壳处理(删掉的凸包要拷贝出来),可以优化到
O(nlogn)
O
(
n
log
n
)
。
但以上做法时空常数巨大还很不好写。。。出题人又没卡,所以就懒得写了。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
#define N 300010
using namespace std;
const int mod=998244353;
const int R=(1<<19)-1;
int type,m,cnt;
int read()
{
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
struct pt
{
int x,y;
ll operator *(pt b){return (ll)x*b.y-(ll)y*b.x;}
bool operator <(pt b)
{
if(x==b.x) return y<b.y;
return x<b.x;
}
pt operator +(pt b)
{
return (pt){x+b.x,y+b.y};
}
pt operator -(pt b)
{
return (pt){x-b.x,y-b.y};
}
}st[N];
struct cvx
{
vector<pt> t,s;
int D;
void build()
{
s.clear();
s=t;
sort(s.begin(),s.end());
int top=0,tmp;
for(int i=0;i<s.size();i++)
{
while(top>1&&(s[i]-st[top])*(st[top]-st[top-1])<=0) top--;
st[++top]=s[i];
}
D=top;
for(int i=s.size()-1;i>=0;i--)
{
while(top>D&&(s[i]-st[top])*(st[top]-st[top-1])<=0) top--;
st[++top]=s[i];
}
s.resize(top);
for(int i=1;i<=top;i++)
s[i-1]=st[i];
}
ll qry(pt p)
{
ll re=-1e18;
int L=0,R=D-1;
while(L<R)
{
int mid=(L+R)>>1;
if(p*s[mid+1]-p*s[mid]<=0) R=mid;
else L=mid+1;
}
re=max(re,p*s[L]);
L=D;R=s.size()-2;
while(L<R)
{
int mid=(L+R)>>1;
if(p*s[mid+1]-p*s[mid]<=0) R=mid;
else L=mid+1;
}
re=max(re,p*s[L]);
return re;
}
}f[R+N];
void add(pt p)
{
cnt++;
for(int i=R+cnt;i;i>>=1)
f[i].t.push_back(p);
for(int i=R+cnt;i;i>>=1)
{
f[i].build();
if(!(i&1)) break;
}
}
void del()
{
for(int i=R+cnt;i;i>>=1)
f[i].t.pop_back();
for(int i=R+cnt;i;i>>=1)
if(!(i&1)) break;
cnt--;
}
ll qry(int x,int l,int r,int lx,int rx,pt p)
{
if(l==lx&&r==rx) return f[x].qry(p);
int mid=(l+r>>1);
if(rx<=mid) return qry(x<<1,l,mid,lx,rx,p);
if(lx>mid) return qry(x<<1|1,mid+1,r,lx,rx,p);
return max(qry(x<<1,l,mid,lx,mid,p),qry(x<<1|1,mid+1,r,mid+1,rx,p));
}
int main()
{
type=read();m=read();
int ans=0;
while(m--)
{
int opt=read(),l,r,x,y;
if(opt==1)
{
x=read();y=read();
add((pt){x,y});
}
if(opt==2) del();
if(opt==3)
{
l=read();r=read();x=read();y=read();
ll tmp=qry(1,1,R+1,l,r,(pt){x,y});
ans^=(tmp%mod+mod)%mod;
}
}
printf("%d",ans);
return 0;
}