[bzoj3600]没有人的算术
2016-10-29 17:36
363 查看
题目大意
定义一种数,要么是0,要么是一个二元组,这个二元组两元都是数。定义小于是:
1、0<(l,r)
2、如果x<a,那么(x,y)<(a,b)
3、如果x=a,y<b,那么(x,y)<(a,b)
定义等于是:
1、0=0
2、如果x=a,y=b,那么(x,y)=(a,b)
大于与小于类似
现在有一个序列,初始全部为0。
有两种操作:
1、把a[k]修改为(a[l],a[r])
2、询问[l,r]最大的数的坐标,多个最大值输出最小坐标
重量平衡树
对于序列带修改和区间最值询问,我们容易想到利用线段树。可是如何快速比较两个数的大小呢?
假如我们把所有数都放入平衡树中,中序遍历就是数的大小顺序,那么两个数的大小比较可以直接比较在平衡树中的rank。
但是我们插入平衡树中也要进行数大小比较,这要怎么办?
我们想,我们能不能O(1)比较两个数的大小?
定义一种映射f(x),保证如果x<y,那么f(x)<f(y)
那么假如我们得到了平衡树,让每个节点都对应一个开区间,其中根节点对应(0,1)。
假如一个节点对应开区间是(l,r),mid=(l+r)/2
那么左儿子对应开区间是(l,mid),右儿子对应开区间是(mid,r)
那么定义f(x)=(l+r)/2
节点x上面的f就是开区间的中点
那么因为是开区间,所以一个节点左子树内所有节点的f值小于本身的f值,右子树内所有节点的f值大于本身的f值。
有了f,我们可以o(1)比较两个数的大小。
每次插入新数,因为新数由两个本身存在于平衡树中的数组成,因此可以拿新数和旧数进行比较。
不过我们现在有一个问题,因为平衡树的形态会被改变,所以f值可能会变,那怎么办?
我们可以使用重量平衡树,例如Treap和替罪羊树。
重量平衡树一次调整代价是log n,所以暴力重构这log n个节点的f值即可。
注意,平衡树中不应存在两个大小相同的数,而且要注意0是特殊的。
#include<cstdio> #include<algorithm> #define fo(i,a,b) for(i=a;i<=b;i++) using namespace std; typedef double db; const int maxn=500000+10; db f[maxn],L[maxn],R[maxn]; int key[maxn][2],tree[maxn][2],fix[maxn],pos[maxn]; int num[maxn*4]; int i,j,k,l,r,s,t,n,m,tot,root; char ch; 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; } char get(){ char ch=getchar(); while (ch!='C'&&ch!='Q') ch=getchar(); return ch; } void left_rotate(int &x){ int y=tree[x][1]; tree[x][1]=tree[y][0]; tree[y][0]=x; L[y]=L[x];R[y]=R[x]; x=y; //if (y==root) root=x; } void right_rotate(int &x){ int y=tree[x][0]; tree[x][0]=tree[y][1]; tree[y][1]=x; L[y]=L[x];R[y]=R[x]; x=y; //if (y==root) root=x; } void insert(int &x,int l,int r,db a,db b){ if (!x){ x=++tot; key[tot][0]=l; key[tot][1]=r; L[tot]=a; R[tot]=b; fix[tot]=rand(); s=x; return; } db c=(a+b)/2; int t; if (x!=1&&f[l]==f[key[x][0]]&&f[r]==f[key[x][1]]){ s=x; return; } if (x!=1&&(f[l]<f[key[x][0]]||f[l]==f[key[x][0]]&&f[r]<f[key[x][1]])){ insert(tree[x][0],l,r,a,c); if (fix[tree[x][0]]<fix[x]) right_rotate(x); } else{ insert(tree[x][1],l,r,c,b); if (fix[tree[x][1]]<fix[x]) left_rotate(x); } } void rebuild(int x){ f[x]=(L[x]+R[x])/2; if (tree[x][0]){ L[tree[x][0]]=L[x]; R[tree[x][0]]=f[x]; rebuild(tree[x][0]); } if (tree[x][1]){ L[tree[x][1]]=f[x]; R[tree[x][1]]=R[x]; rebuild(tree[x][1]); } } void build(int p,int l,int r){ if (l==r){ num[p]=l; return; } int mid=(l+r)/2; build(p*2,l,mid); build(p*2+1,mid+1,r); if (f[pos[num[p*2]]]>=f[pos[num[p*2+1]]]) num[p]=num[p*2];else num[p]=num[p*2+1]; } void change(int p,int l,int r,int a){ if (l==r) return; int mid=(l+r)/2; if (a<=mid) change(p*2,l,mid,a);else change(p*2+1,mid+1,r,a); if (f[pos[num[p*2]]]>=f[pos[num[p*2+1]]]) num[p]=num[p*2];else num[p]=num[p*2+1]; } int query(int p,int l,int r,int a,int b){ if (l==a&&r==b) return num[p]; int mid=(l+r)/2; if (b<=mid) return query(p*2,l,mid,a,b); else if (a>mid) return query(p*2+1,mid+1,r,a,b); else{ int j=query(p*2,l,mid,a,mid),k=query(p*2+1,mid+1,r,mid+1,b); if (f[pos[j]]>=f[pos[k]]) return j;else return k; } } int main(){ freopen("data.in","r",stdin);freopen("data.out","w",stdout); srand(233); n=read();m=read(); root=tot=1; L[1]=0; R[1]=1; f[1]=0.5; fix[1]=rand(); fo(i,1,n) pos[i]=1; build(1,1,n); fo(i,1,m){ ch=get(); if (ch=='C'){ l=read();r=read();k=read(); insert(root,pos[l],pos[r],0,1); pos[k]=s; rebuild(pos[k]); change(1,1,n,k); } else{ l=read();r=read(); printf("%d\n",query(1,1,n,l,r)); } } }
相关文章推荐
- bzoj 3600 没有人的算术 - 替罪羊树 - 线段树
- 【题解】BZOJ 3600: 没有人的算术——替罪羊树、线段树
- bzoj 3600: 没有人的算术 (替罪羊树)
- bzoj 3600: 没有人的算术 替罪羊树
- bzoj3600 没有人的算术
- 【替罪羊树-动态标号+线段树】BZOJ3600[没有人的算术]题解
- [BZOJ3600][线段树][替罪羊树]没有人的算术
- [bzoj3600]没有人的算术 替罪羊树+线段树
- [BZOJ3600]没有人的算术 重量平衡树+线段树
- bzoj 3600: 没有人的算术 替罪羊树+线段树
- BZOJ 3600 没有人的算术
- 【bzoj3600】没有人的算术
- [bzoj3600]没有人的算术
- bzoj 3600 - 没有人的算术
- [BZOJ3600][没有人的算术][替罪羊树+线段树]
- bzoj 3600: 没有人的算术
- [替罪羊树 动态标号 线段树] BZOJ 3600 没有人的算术
- [题解]bzoj3600 没有人的算数
- [题解]bzoj3600 没有人的算数
- [置顶] 3600: 没有人的算术