[bzoj3600]没有人的算术

来源:互联网 发布:奇爱博士 知乎 编辑:程序博客网 时间:2024/05/17 08:01

题目大意

定义一种数,要么是0,要么是一个二元组,这个二元组两元都是数。
定义小于是:
1、0<(l,r)
2、如果x<a,那么(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));        }    }}
0 0
原创粉丝点击