您的位置:首页 > 其它

[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));
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: