[bzoj3600]没有人的算术

9 篇文章 0 订阅
1 篇文章 0 订阅

题目大意

定义一种数,要么是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));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值