• noi2017 T1 整数 ——线段树


    loj.ac上有  题目传送门

    不过我还是把题目搬过来吧

    整数(integer)
    【题目背景】
    在人类智慧的山巅,有着一台字长为 1048576 位的超级计算机,著名理论计算机科 学家 P 博士正用它进行各种研究。不幸的是,这天台风切断了电力系统,超级计算机 无法工作,而 P 博士明天就要交实验结果了,只好求助于学过 OI 的你......
    【题目描述】 P 博士将他的计算任务抽象为对一个整数的操作。

     具体来说,有一个整数 x ,一开始为 0。 接下来有 n 个操作,每个操作都是以下两种类型中的一种:

     • 1 a b :将 x 加上整数 a·2b,其中 a 为一个整数,b 为一个非负整数

     • 2 k :询问 x 在用二进制表示时,位权为 2k 的位的值(即这一位上的 1 代表 2k )

     保证在任何时候,x≥0。
    【输入格式】 从标准输入读入数据。

     输入的第一行包含四个正整数 n,t1,t2,t3,n 的含义见题目描述,t1,t2,t3 的具体含义 见子任务。

     接下来 n 行,每行给出一个操作,具体格式和含义见题目描述。

    同一行输入的相邻两个元素之间,用恰好一个空格隔开。
    【输出格式】

    输出到标准输出。

    对于每个询问操作,输出一行,表示该询问的答案(000 或 111)。 对于加法操作,没有任何输出

    样例输入 1

     10 3 1 2

     1 100 0

     1 2333 0

     1 -233 0

     2 5

     2 7

     2 15

     1 5 15

     2 15

     1 -1 12

     2 15

    【样例 1 输出】

     0

     1

     0

     1

     0

    【子任务】 在所有测试点中,

    1 ≤ t1 ≤ 3,1 ≤ t2 ≤ 4,1 ≤ t3 ≤ 2。

    不同的 t1,t2,t3 对应的特殊限制 如下:

    • 对于 t1 = 1 的测试点,满足 a = 1

    •对于 t1 = 2 的测试点,满足 |a|= 1

    • 对于 t1 = 3 的测试点,满足 |a|≤109 • 对于 t2 = 1 的测试点,满足 0≤b,k≤30

    • 对于 t2 = 2 的测试点,满足 0≤b,k≤100

    • 对于 t2 = 3 的测试点,满足 0≤b,k≤n

    • 对于 t2 = 4 的测试点,满足 0≤b,k≤30n

    • 对于 t3 = 1 的测试点,保证所有询问操作都在所有修改操作之后

    • 对于 t3 = 2 的测试点,不保证询问操作和修改操作的先后顺序

    这道题首先 a*2^b 这个东西呢按我的想法是把 a 拆成2的x次幂的和 然后+b

    如果只有加法当然很好写 但是这道题 a 可能是负的 

    所以我开了两个数组 s1 s2 来存 s1 存的是正的 s2 存的是负的

    注意存的时候进位 均摊复杂度只有o(1) 所以暴力进位就好了

    然后问题就转换成了已知两个高精度数,问他们的差的某一位是什么

     然后分类可得

    我们记一个数为 1(0)x

    1 (0)  表示询问的这一位的值(因为是二进制,所以不是1就是0)

    x就是询问位后面那一串东西(一坨1 0 混合体)

    考虑减法是否退位的时候呢

    1. x>=y:
    1x-1y=0(x-y)
    1x-0y=1(x-y)
    0x-1y=1(x-y)
    0x-0y=0(x-y)
    2. x<y:
    1x-1y=1(x-y+2^k)
    1x-0y=0(x-y+2^k)
    0x-1y=0(x-y+2^k)
    0x-0y=1(x-y+2^k)

    举个例子吧

    10-10=00
    10-00=10
    01-10=11
    01-01=00

    10-11=11
    10-01=01
    00-11=01
    00-01=11

    注 题目保证x始终大于等于0 所以s1总和也一定大于s2

    这样分析之后呢 问题就转换成了从1-x(即询问位)-1这个区间里s1 s2 的大小关系

    如果 s1>=s2

    那么答案就是 s1^s2(^念做异或)

    如果 s2<=s2

    那么答案就是 s1==s2 (==念做同或)

    那么 x 和 y 的比较 其实就是比较字典序 找到第一个不一样的地方 哪个是1 哪个就大 (这么很好证明我就不详细讲了) 注意这里讲的都是二进制

    算了举个例子吧 如果x是1000 y是0111 那么x是不是一定大于y 

    所以我们可以采用线段树维护区间内s1 s2 的大小

    当然我这里采用的是zkw线段树 (自带底层优化自然会比较快 而且修改也很好写)

    毕竟三千万(3e7)个叶子结点 同时线段树中下标也就是二进制中 2的次数

    操作自然就是 区间内单点修改,前缀查询

    那么我们每次操作1拆完a加上b以及进位之后

    我们要记录一波 修改到的最左位置到最右位置 进位自然也算修改

    然后就在线段树上暴力修改顺便上传信息 复杂度是 o( r-l + log(n))

    操作2 询问的时候我们就找到区间中靠右(也就是比较大的位置)的第一个不一样的地方然后判断一波就好了

    到这里题目就完美解决了 修改(modify)以及 查找(find) 就看代码吧 

    注: find我的写法呢 是向上走时,如果x是右孩子且x的兄弟为1才向下走

    实测跑得也蛮快的 23333

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long 
    using namespace std;
    const int M=3e7+31,N=1<<25;
    int read(){
        int ans=0,f=1,c=getchar();
        while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
        while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=getchar();}
        return ans*f;
    }
    int s1[M],s2[M],cnt;
    int l,r,a,b,n,k;
    void prepare(int x){
        if(!x) return ;
        int v=x>0?x:-x,w[31]; 
        cnt=0;
        for(int i=30;i>=0;i--){
            int now=1<<i;
            if(now&v) v-=now,w[++cnt]=i;
            if(!v) break;
        }
        l=w[cnt]+b; r=w[1]+b;
        if(x>0)
        for(int i=1;i<=cnt;i++){ 
            int now=w[i]+b; 
            while(s1[now]) s1[now++]=0,r=max(r,now);
            s1[now]=1;
        }
        else
        for(int i=1;i<=cnt;i++){
            int now=w[i]+b; 
            while(s2[now]) s2[now++]=0,r=max(r,now);
            s2[now]=1;
        }
    }
    int tr[2*N],T,now;
    void modify(){
        for(int i=l;i<=r;i++) tr[i+N]=s1[i]^s2[i];
        for(l=(l+N)>>1,r=(r+N)>>1;l;l>>=1,r>>=1)
        for(int i=l;i<=r;i++) tr[i]=tr[i<<1]|tr[i<<1^1];
    }
    int find(int k){
        for(k+=N;k;k>>=1) if(k&1&tr[k^1]){
            for(k^=1;k<N;k=k<<1^tr[k<<1^1]);
            return k-N;
        }
        return -1;
    }
    int main()
    {
        n=read(); T=read(); T=read(); T=read();
        for(int i=1;i<=n;i++){
            k=read();
            if(k==1){
                a=read(); b=read(); 
                prepare(a);
                modify();
            }
            else{
                a=read(); 
                now=find(a);
                if(s1[now]>s2[now]||now==-1) printf("%d
    ",s1[a]^s2[a]);
                else printf("%d
    ",s1[a]==s2[a]);
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    ecshop简单结构
    关于dwt文件和lbi文件
    ecshop各个文件夹作用
    修改ecshop模板体会
    PHP的字符串函数
    数组的相关处理函数
    PHP连接数据库
    Headless谷歌selenium使用
    关于无界面浏览器、request模块、tesseract模块的部分笔记
    爬取某城市公交钱路--xpath过滤
  • 原文地址:https://www.cnblogs.com/lyzuikeai/p/7224090.html
Copyright © 2020-2023  润新知