Unknown
Description
原题目名字是“我们仍未知道那天所看见的数据结构的名字”,由于原题目名太长就叫Unknown了……
我们,渐渐地长大了。在这缓缓逝去的季节里,屏幕上闪烁的字符,也在静静地变化着。
那个季节里编写的数据结构,叫什么名字来着呢?
慢慢地,OI渐渐地淡去。而我们则在不断成长,但是那个程序一定仍在某个时空里继续运行着。
Salroey忘了那个数据结构的名字和内容,但她却记得题目,于是她来寻求你的帮助。
有一个元素为向量的序列,下标从1开始,初始时为空,现在你需要支持三个操作:
1.在SS的末尾添加一个元素(x,y)(x,y)。
2.删除SS的末尾元素。
3.询问下标在[l,r][l,r]区间内的元素中,(x,y)×Si(x,y)×Si的最大值。
其中××表示向量的叉积,(x1,y1)×(x2,y2)=x1y2−x2y1(x1,y1)×(x2,y2)=x1y2−x2y1
Input
第一行一个整数 tptp 表示数据类型,接下来输入多组数据(不超过 33 组),以 m=0m=0 结束,对于每组数据:
第一行一个整数 mm 表示操作数。
接下来行,每行有三种格式:
1 x y 在的末尾添加一个元素 (x,y)(x,y)
2 删除的末尾元素
3 l r x y 询问下标在区间 [l,r][l,r] 内的元素中,(x,y)×Si(x,y)×Si的最大值。
Output
为避免过多输出,你要将所有询问答案对M=998244353取模(数学意义上,取模的结果在[0,M)区间内),并输出取模后答案的异或和,每组数据一行。
Sample Input
7
6
1 -5 10
3 1 1 7 -9
2
1 2 6
1 -3 5
3 1 2 10 -7
0
Sample Output
83
Hint
对于所有数据:
0≤tp≤7,1≤m≤5000000≤tp≤7,1≤m≤500000。
任意时刻 n≥0n≥0, nn 为当前序列长度。
1操作个数不超过300000300000,且满足 −109≤x≤109;1≤y≤109−109≤x≤109;1≤y≤109。
3操作个数不超过300000,且满足 1≤x≤109;−109≤y≤1091≤x≤109;−109≤y≤109。
m≤500000,内存限制为64M
WA也就算了,TLE也不管了,MLE咱也认了,但是……
这是什么意思啊啊啊啊啊啊啊
(╯‵□′)╯︵┻━┻
思路:
考虑把这个奇怪的叉积弄好看一点:
设三个向量分别为
(x,y)
,
(x1,y1)
,
(x2,y2)
,其中
x2<x1
:
(x,y)×(x1,y1)>(x,y)×(x2,y2)
xy1−x1y>xy2−x2y
x(y1−y2)>y(x1−x2)
注意到
x≥1
,那么
y1−y2x1−x2>yx
那么就可以建凸包,并用斜率二分了~
然而这个凸包不好建。
因为如果一段区间的点全部因为不够优而被弹掉了的话,那将询问不到任何东西。
那么显然要试图用尽可能少的凸包来描述一段区间。
考虑使用线段树来执行这个过程。
两个儿子分别建立凸包,再在父亲处用类似归并排序的方法合并。
但是考虑到这样一次经过
O(logn)
个区间,每个区间重构
O(n)
,复杂度无法承受。
观察发现,一段区间有可能被纳入答案,当且仅当现有的点数大于等于其右区间。
那么考虑优化成,一旦区间被填满,重构当前区间凸包。
然而这样做最坏复杂度仍是与上面相同的,只需要造一组在区间右端点反复横跳的数据就能卡掉这个方法。
考虑引入替罪羊思想,只有当同一深度的下一个区间被填满了才重构当前区间。
询问时若扫到未重构的区间,递归左右儿子继续寻找答案。
可以发现这样仍会落在
O(logn)
个区间上,因为只有最右边的一列区间有没被更新过的可能,而这样的区间只有
O(logn)
个。
然后每次询问在访问到的区间的凸包上二分查找最优解,总询问复杂度 O(log22n)
最后是一些小细节:
如果直接在一个区间被填满时重构上一个区间有可能会出问题。
因为此时咱们的树未必是一颗满二叉树,有些区间不存在“下一个区间”,无法被重构,在某些写法的情况下会WA(比如咱)。
解决方法只有一种:开成满二叉树,然后动态开空间。
但这样做就有可能获得满屏鲜红的Dangerous Syscalls
还咱一晚上青春啊啊啊
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=(1<<19)+10;
const int n=(1<<19);
const ll md=998244353;
#define mid ((l+r)>>1)
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x*f;
}
inline void chkmax(ll &a,ll b){if(a<b)a=b;}
struct point
{
int x,y;
point(){}
point(int _x,int _y){x=_x;y=_y;}
bool operator < (const point &o)const
{
if(x==o.x)return y<o.y;
return x<o.x;
}
point operator - (const point &o)const
{
return point(x-o.x,y-o.y);
}
friend ll cross(const point &a,const point &b)
{
return (ll)a.x*(ll)b.y-(ll)b.x*(ll)a.y;
}
};
struct hull
{
point *stk;
int top;
inline void init(int siz)
{
stk=new point[siz+1];
top=0;
}
inline void clear(){top=0;}
const point &operator [] (int x)const{return stk[x];}
inline void push_back(const point &o)
{
while(top>1 && cross(o-stk[top-1],stk[top]-stk[top-1])<=0)
top--;
stk[++top]=o;
}
inline void merge(const hull &a,const hull &b)
{
delete stk;
init(a.top+b.top+1);
for(int pa=1,pb=1;pa<=a.top || pb<=b.top;)
{
if(pb>b.top || (pa<=a.top && a[pa]<b[pb]))
push_back(a[pa++]);
else
push_back(b[pb++]);
}
}
inline ll query(const point &o)
{
int l=1,r=top;
while(l<r)
{
if(cross(o,stk[mid+1]-stk[mid])>=0)
l=mid+1;
else
r=mid;
}
return cross(o,stk[l]);
}
};
struct segment_tree
{
hull t[N<<2];
int top,lborder[30];
inline void build(int x,int l,int r,int dep=0)
{
if(l==1)lborder[dep]=x;
if(l==r){t[x].init(r-l+1);return;}
build(x<<1,l,mid,dep+1);
build(x<<1|1,mid+1,r,dep+1);
}
inline void init(int x,int l,int r)
{
if(l==r){t[x].top=0;return;}
t[x].clear();
init(x<<1,l,mid);
init(x<<1|1,mid+1,r);
}
inline void insert(int x,int l,int r,int p,const point &o,int dep=0)
{
if(l==r)
{
t[x].top=0;
t[x].push_back(o);
return;
}
if(p<=mid)
insert(x<<1,l,mid,p,o,dep+1);
else
insert(x<<1|1,mid+1,r,p,o,dep+1);
if(p==r)
if(x!=lborder[dep] && !t[x-1].top)
t[x-1].merge(t[(x-1)<<1],t[(x-1)<<1|1]);
}
inline ll query(int x,int l,int r,int dl,int dr,const point &o)
{
if(t[x].top && dl<=l && r<=dr)
return t[x].query(o);
ll ret=-1e18;
if(dl<=mid)
chkmax(ret,query(x<<1,l,mid,dl,dr,o));
if(mid<dr)
chkmax(ret,query(x<<1|1,mid+1,r,dl,dr,o));
return ret;
}
inline void del(int x,int l,int r,int p,int dep=0)
{
t[x].clear();
if(l==r)return;
if(p<=mid)
del(x<<1,l,mid,p,dep+1);
else
del(x<<1|1,mid+1,r,p,dep+1);
}
}t;
int tp,m,top;
int main()
{
t.build(1,1,n);
tp=read();
while(m=read())
{
top=0;t.init(1,1,n);
int ty,l,r;point p;
ll lans=0,ans;
for(int i=1;i<=m;i++)
{
ty=read();
if(ty==1)
{
p.x=read();p.y=read();
t.insert(1,1,n,++top,p);
}
else if(ty==2)
t.del(1,1,n,top--);
else
{
l=read();r=read();
p.x=read();p.y=read();
ans=t.query(1,1,n,l,r,p);
ans=(ans%md+md)%md;
lans^=ans;
}
}
printf("%lld\n",lans);
}
return 0;
}