[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咱也认了,但是……
这是什么意思啊啊啊啊啊啊啊
Dangerous Syscalls

(╯‵□′)╯︵┻━┻


思路:
考虑把这个奇怪的叉积弄好看一点:
设三个向量分别为(x,y),(x1,y1),(x2,y2),其中x2<x1
(x,y)×(x1,y1)>(x,y)×(x2,y2)
xy1x1y>xy2x2y
x(y1y2)>y(x1x2)
注意到x1,那么
y1y2x1x2>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;}
原创粉丝点击