【uoj#191.】Unknown
论文题,手残+脑残根本写不出来点分写法。
论文里5.14好像有个优雅的写法。
考虑用线段树维护凸包,1操作就是往线段树最底层加点,每把一根线段加满就合并。这样显然是不行的,只要在一个点左右横跳就会t。
然后考虑每次不立即合并,而是等到下一次同层的线段需要合并的时候再进行合并,这样不管怎么搞都是nlog2n。具体势能分析不会,所以就简单证一下。
插入&删除时,考虑合并一条长度为L的线段,至少要进行L次插入,平均下来对每层的贡献就是O(1),共logn层,所以每次的复杂度是logn的,总复杂度nlogn
查询时,考虑一个区间会被分成log条线段,考虑每条线段的情况:
1、已经被合并
2、没有被合并,两个子区间已经合并。
3、没有被合并,左子区间已经被合并。
由于只会在右端插入,所以左区间肯定比右区间先合并,所以最坏情况下只需要递归右区间。
1、2都是O(1)直接求,3需要递归,共log层,单次查询复杂度log2n,总复杂度nlog2n。
uoj上好像并没有把空间卡到64M;
#include<bits/stdc++.h> #define maxn 5000005 #define ll long long #define INF 1ll<<62 #define MOD 998244353 using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int n=524288; struct P{ int x,y; P(int x,int y):x(x),y(y){}; P(){}; bool operator < (const P &tmp)const{if(x==tmp.x)return y<tmp.y;return x<tmp.x;} }pool[maxn],*tt,*a[n<<1]; ll Cross(P a,P b){return 1ll*a.x*b.y-1ll*a.y*b.x;} inline int calc(P a,P b,P c){ int up1=b.y-a.y,up2=c.y-b.y; int down1=b.x-a.x,down2=c.x-b.x; return 1ll*up1*down2<=1ll*up2*down1; } inline int calc2(P a,P b,P c){ int up1=b.y-a.y,up2=c.y; int down1=b.x-a.x,down2=c.x; return 1ll*up1*down2>=1ll*up2*down1; } inline void push(P *a,int &ta,P p){ while(ta>1&&calc(a[ta-1],a[ta],p))--ta; a[++ta]=p; } int TOT; inline void merge(P *&a,int &ta,P *b,int tb,P *c,int tc){ a=tt;ta=0; int t1=1,t2=1; while(t1<=tb||t2<=tc){ if(t1<=tb){ if(t2>tc||b[t1]<c[t2])push(a,ta,b[t1++]); else push(a,ta,c[t2++]); }else push(a,ta,c[t2++]); } tt+=ta; } int tot[maxn],last[maxn]; inline void insert(int o,int l,int r,int x,int dep,P v){ if(l==r){ a[o]=tt++;tt->x=v.x;tt->y=v.y;tot[o]=1; return; } int mid=l+r>>1; if(x<=mid)insert(o<<1,l,mid,x,dep+1,v); else insert(o<<1|1,mid+1,r,x,dep+1,v); if(x==r){ if(last[dep]){ int t=last[dep]; merge(a[t],tot[t],a[t<<1],tot[t<<1],a[t<<1|1],tot[t<<1|1]); } last[dep]=o; } } inline void erase(int o,int l,int r,int x,int dep){ tot[o]=0; if(l==r)return; if(last[dep]==o)last[dep]=0; int mid=l+r>>1; if(x<=mid)erase(o<<1,l,mid,x,dep+1); else erase(o<<1|1,mid+1,r,x,dep+1); } inline ll query(P *a,int tot,P p){ int l=1,r=tot; while(l<=r){ int mid=l+r>>1; if(mid==tot)return Cross(p,a[mid]); if(calc2(a[mid],a[mid+1],p))l=mid+1; else r=mid-1; } return Cross(p,a[l]); } ll Max; inline void query(int o,int l,int r,int x,int y,P v){ if(tot[o]&&x<=l&&r<=y){ Max=max(Max,query(a[o],tot[o],v)); return; } int mid=l+r>>1; if(x<=mid)query(o<<1,l,mid,x,y,v); if(y>mid)query(o<<1|1,mid+1,r,x,y,v); } int m,Ans; void WoRk(){ int type,f1,f2,f3,f4; for(int i=1;i<=m;++i){ type=read(); if(type==1){ f1=read();f2=read(); insert(1,1,n,++TOT,1,P(f1,f2)); }else if(type==2){ erase(1,1,n,TOT--,1); }else{ f1=read();f2=read();f3=read();f4=read(); Max=-INF;query(1,1,n,f1,f2,P(f3,f4)); Ans^=(Max%MOD+MOD)%MOD; } } printf("%d\n",Ans); } void Init(){ tt=pool;TOT=Ans=0; memset(last,0,sizeof(last)); memset(tot,0,sizeof(tot)); } int main(){ int Illyasviel_Von_Einzbern=read(); for(;;){ m=read(); if(m==0)return 0; Init(); WoRk(); } return 0; }