bzoj 3600 - 没有人的算术
2017-01-17 16:58
344 查看
Description
blablabla开个脑洞:
题目讲了两页跟圣经有关的东东
替罪羊的名字又来自圣经
所以这题是替罪羊
Analysis
第二问求区间最大值用线段树就可以搞定难度在第一问
不难想到合并时double乱搞搞出一个代表它的大小作为映射
然后当然会爆精度-.-
Solution
1.不难发现,对于将x,y合并成(x,y)的操作中我们把题目中(l,r)稍微改成(rk[l],rk[r])
x的排名和y的排名都是已知的,所以能很快的找到(x,y)应该排第几
这就是平衡树了
2.但是,这样排名还是难以控制
我们可以在一个点上加上一个控制区域
root控制区域为(l,r)
lc[root]控制(0,mid-1),rc[root]控制(mid+1,r)
root映射到得相对大小就是mid
3.但是,有几个问题:
①一旋转这些控制区间就乱套了
②深度大了跟暴力double是一个样的
那我们就用替罪羊树的优美性质,保持重量平衡,且重构时才修改映射
4.但是,因为排名会变,把(l,r)转化成(rk[l],rk[r])存储是不现实的,改为存(pos[l],pos[r]),pos[l]指向替罪羊中的一个点,rk[pos[l]]才是排名
Notice
1.替罪羊的 深度,字数大小 都是均摊log的2.算映射mid时小心l+r爆炸
3.重构时虽然映射变了,但其中的相对大小关系不变,是不用修改线段树的
Code
#include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <cctype> #include <algorithm> using namespace std; typedef long long LL; typedef double db; const int M=500007; const db alpha=0.75; const LL INF=9223372036854775807; inline int rd(){ int x=0;bool f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-')f=0; for(;isdigit(c);c=getchar()) x=x*10+c-48; return f?x:-x; } int n,m; char s[7]; int pos[M];//pos[i]表示a[i]对应的treap中节点 LL rk[M];//treap中节点映射得到的一个排名 struct node{ int x,y; node(int xx=0,int yy=0){x=xx;y=yy;} }; bool operator ==(node x,node y){return rk[x.x]==rk[y.x]&&rk[x.y]==rk[y.y];} bool operator < (node x,node y){return rk[x.x]<rk[y.x]||(rk[x.x]==rk[y.x]&&rk[x.y]<rk[y.y]);} struct ScapeGt{ node val[M];//用于treap比较大小 int lc[M],rc[M]; int sz[M]; int root,tot; int *sgt; LL ssl,ssr; int que[M],tque; bool isbad(int x){ return (db)sz[x]*alpha<(db)max(sz[lc[x]],sz[rc[x]]); } void pushup(int x){ sz[x]=1+sz[lc[x]]+sz[rc[x]]; } void dfs(int x){ if(lc[x]) dfs(lc[x]); que[++tque]=x; if(rc[x]) dfs(rc[x]); } int build(int l,int r,LL sl,LL sr){ if(l>r) return 0; int mid=l+r>>1; LL smid=sl+(sr-sl)/2; int x=que[mid]; sz[x]=1; rk[x]=smid; lc[x]=build(l,mid-1,sl,smid-1); rc[x]=build(mid+1,r,smid+1,sr); pushup(x); return x; } void rebuild(int *x,LL l,LL r){ tque=0; dfs(*x); *x=build(1,tque,l,r); } int insert(int &x,node d,LL l,LL r){ LL mid=l+(r-l)/2; if(!x){ x=++tot; val[x]=d; sz[x]=1; lc[x]=rc[x]=0; rk[x]=mid; return x; } if(val[x]==d) return x; int tp; if(d<val[x]) tp=insert(lc[x],d,l,mid-1); else tp=insert(rc[x],d,mid+1,r); pushup(x); if(isbad(x)) sgt=&x,ssl=l,ssr=r; return tp; } int ins(node d){ sgt=0; int x=insert(root,d,0,INF);//前面写-INF算mid会炸掉 if(sgt) rebuild(sgt,ssl,ssr); return x; } }SGT; struct segtree{ int mx[M]; int gmx(int x,int y){ if(rk[pos[x]]==rk[pos[y]]) return x<y?x:y; return rk[pos[x]]>rk[pos[y]]?x:y; } void pushup(int x){ mx[x]=gmx(mx[x<<1],mx[x<<1|1]); } void build(int x,int l,int r){ if(l==r){ mx[x]=l; return; } int mid=l+r>>1; build(x<<1,l,mid); build(x<<1|1,mid+1,r); pushup(x); } void mdf(int x,int l,int r,int to){ if(l==r) return; int mid=l+r>>1; if(to<=mid) mdf(x<<1,l,mid,to); else mdf(x<<1|1,mid+1,r,to); pushup(x); } int get(int x,int l,int r,int tl,int tr){ if(tl<=l&&r<=tr) return mx[x]; int mid=l+r>>1; if(tl<=mid&&mid<tr) return gmx(get(x<<1,l,mid,tl,tr),get(x<<1|1,mid+1,r,tl,tr)); if(tl<=mid) return get(x<<1,l,mid,tl,tr); if(mid<tr) return get(x<<1|1,mid+1,r,tl,tr); } }Seg; int main(){ int i,x,y,z; n=rd(), m=rd(); SGT.ins(node(0,0)); for(i=1;i<=n;i++) pos[i]=1; Seg.build(1,1,n); for(i=1;i<=m;i++){ scanf("%s",s); if(s[0]=='C'){ x=rd(),y=rd(),z=rd(); node nw=node(pos[x],pos[y]); pos[z]=SGT.ins(nw); Seg.mdf(1,1,n,z); } else{ x=rd(),y=rd(); printf("%d\n",Seg.get(1,1,n,x,y)); } } return 0; }
相关文章推荐
- bzoj 3600 没有人的算术 - 替罪羊树 - 线段树
- 【题解】BZOJ 3600: 没有人的算术——替罪羊树、线段树
- [BZOJ3600][线段树][替罪羊树]没有人的算术
- [bzoj3600]没有人的算术
- bzoj3600 没有人的算术
- 【替罪羊树-动态标号+线段树】BZOJ3600[没有人的算术]题解
- BZOJ 3600 没有人的算术
- [bzoj3600]没有人的算术 替罪羊树+线段树
- [BZOJ3600]没有人的算术 重量平衡树+线段树
- 【bzoj3600】没有人的算术
- bzoj 3600: 没有人的算术 替罪羊树+线段树
- [BZOJ3600][没有人的算术][替罪羊树+线段树]
- [替罪羊树 动态标号 线段树] BZOJ 3600 没有人的算术
- [bzoj3600]没有人的算术
- bzoj 3600: 没有人的算术
- bzoj 3600: 没有人的算术 替罪羊树
- bzoj 3600: 没有人的算术 (替罪羊树)
- 3600: 没有人的算术
- [题解]bzoj3600 没有人的算数
- [题解]bzoj3600 没有人的算数