[UOJ191][集训队互测2016]Unknown-线段树-斜率优化
来源:互联网 发布:沙特王室知乎 编辑:程序博客网 时间:2024/05/17 06:10
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咱也认了,但是……
这是什么意思啊啊啊啊啊啊啊
(╯‵□′)╯︵┻━┻
思路:
考虑把这个奇怪的叉积弄好看一点:
设三个向量分别为
注意到
那么就可以建凸包,并用斜率二分了~
然而这个凸包不好建。
因为如果一段区间的点全部因为不够优而被弹掉了的话,那将询问不到任何东西。
那么显然要试图用尽可能少的凸包来描述一段区间。
考虑使用线段树来执行这个过程。
两个儿子分别建立凸包,再在父亲处用类似归并排序的方法合并。
但是考虑到这样一次经过
观察发现,一段区间有可能被纳入答案,当且仅当现有的点数大于等于其右区间。
那么考虑优化成,一旦区间被填满,重构当前区间凸包。
然而这样做最坏复杂度仍是与上面相同的,只需要造一组在区间右端点反复横跳的数据就能卡掉这个方法。
考虑引入替罪羊思想,只有当同一深度的下一个区间被填满了才重构当前区间。
询问时若扫到未重构的区间,递归左右儿子继续寻找答案。
可以发现这样仍会落在
然后每次询问在访问到的区间的凸包上二分查找最优解,总询问复杂度
最后是一些小细节:
如果直接在一个区间被填满时重构上一个区间有可能会出问题。
因为此时咱们的树未必是一颗满二叉树,有些区间不存在“下一个区间”,无法被重构,在某些写法的情况下会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;}
- [UOJ191][集训队互测2016]Unknown-线段树-斜率优化
- [二进制分组 线段树 || 点分治 分治] UOJ #191 【集训队互测2016】Unknown
- uoj191 Unknown
- 【UOJ 191/集训队互测】Unknown
- 3672: [Noi2014]购票 树剖+线段树+斜率优化
- 【jzoj2902】【集训队互测 2012】【Middle】【可持久化线段树】
- BZOJ 2402 陶陶的难题II 二分答案+斜率优化+树链剖分+线段树维护凸包
- 【bzoj3672】[Noi2014]购票 斜率优化+树链剖分+线段树+凸包+三分
- 【主席树】2012集训队互测 Middle
- 斜率优化
- 斜率优化
- 斜率优化
- 斜率优化
- 斜率优化
- 斜率优化
- 斜率优化
- 斜率优化
- 斜率优化
- Android Menu用法全面讲解
- 常见排序算法总结
- 利用alarm()和pause()函数实现sleep()函数
- odoo10 report自定义paperformat,就是自定义打印的纸张大小格式等
- 排序-快速排序
- [UOJ191][集训队互测2016]Unknown-线段树-斜率优化
- 手机端页面所需文件引入
- 给多个jar包起一个别名,用于项目引用
- springboot整合mybatis
- Photolemur(自动化图片处理软件)官方破解版V2.2.0.909下载 | photolemur 破解版
- Sicily 拓扑排序
- 套接字编程常用函数
- 排序-改良冒泡排序
- 排序-直接插入排序