zoukankan      html  css  js  c++  java
  • NOI 2017 D1T1 整数 压位 线段树

    $ Rightarrow $ 戳我进洛谷原题 $ Rightarrow $ 戳我进BZOJ原题

    [Noi2017]整数

    时空限制 $ quad $ 2000ms / 512MB

     

    题目背景

    在人类智慧的山巅,有着一台字长为 $ 1048576 $ 位(此数字与解题无关)的超级计算机,
    著名理论计算机科学家P博士正用它进行各种研究。不幸的是,这天台风切断了电力系统,
    超级计算机无法工作,而 P 博士明天就要交实验结果了,只好求助于学过OI的你. . . . . .
     

    题目描述

    P 博士将他的计算任务抽象为对一个整数的操作。具体来说,有一个整数 $ x $ ,一开始为 $ 0 $ 。

    接下来有 $ n $ 个操作,每个操作都是以下两种类型中的一种:

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

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

    保证在任何时候,$ x geqslant 0 $ 。
     

    输入输出格式

    输入格式:

    输入的第一行包含四个正整数 $ n,t_1,t_2,t_3 $ ,$ n $ 的含义见题目描述, $ t_1,t_2,t_3 $ 的具体含义见子任务。

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

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

    输出格式:

    对于每个询问操作,输出一行,表示该询问的答案( $ 0 $ 或 $ 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
    

    输出样例

     0
     1
     0
     1
     0
    

     

    说明

    在所有测试点中,$ 1leqslant t_1 leqslant 3, 1 leqslant t_2 leqslant 4, 1 leqslant t_3 leqslant 2 $ 。不同的 $ t_1,t_2,t_3 $ 对应的特殊限制如下:

    • 对于 $ t_1 = 1 $ 的测试点,满足 $ a = 1 $
    • 对于 $ t_1 = 2 $ 的测试点,满足 $ |a| = 1 $
    • 对于 $ t_1 = 3 $ 的测试点,满足 $ |a| leqslant 10^9 $
    • 对于 $ t_2 = 1 $ 的测试点,满足 $ 0 leqslant b, k leqslant 30 $
    • 对于 $ t_2 = 2 $ 的测试点,满足 $ 0 leqslant b, k leqslant 100 $
    • 对于 $ t_2 = 3 $ 的测试点,满足 $ 0 leqslant b, k leqslant n $
    • 对于 $ t_2 = 4 $ 的测试点,满足 $ 0 leqslant b, k leqslant 30n $
    • 对于 $ t_3 = 1 $ 的测试点,保证所有询问操作都在所有修改操作之后
    • 对于 $ t_3 = 2 $ 的测试点,不保证询问操作和修改操作的先后顺序

    本题共 25 个测试点,每个测试点 4 分。各个测试点的数据范围如下:

    pic

    思路

    • 本题需要用到线段树+压位。

    • 首先考虑在某一位加1或减1的情况。
      在加1时,我们要从当前位开始,找到最低的为0的位,然后把这一位加1,路上经过的所有位都清零。
      在减1时,我们要从当前位开始,找到最低的为1的位,然后把这一位减1,路上经过的所有位都修改成1。
      这些操作显然可以在线段树上完成。

    • 但是我们发现,操作的数位的范围可能特别大,达到 $ 3 imes 10^7 $ , $ O(n imes log_n ) $ 的时间复杂度并不能承受。
      那么我们可以把30个二进制位压成一位,或者甚至把60个二进制位压成一位,
      然后在操作的时候,原来的找0就变成了找第一个全不是1的段,原来的找1就变成了找第一个全不是0的段,
      那么压位后一次修改最多涉及两次操作,常数大大降低,就可以通过此题了。
       

    代码

    /**************************************************************
        Problem: 4942
        User: PotremZ
        Language: C++
        Result: Accepted
        Time:23424 ms
        Memory:48168 kb
    ****************************************************************/
     
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 1000005
    const int INF=(1<<30)-1;
    int n,sor[N<<2],sad[N<<2],lzy[N<<2];
    inline void pushdown(int o){
        sor[o<<1]=sor[o<<1|1]=sad[o<<1]=sad[o<<1|1]=lzy[o<<1]=lzy[o<<1|1]=lzy[o];
        lzy[o]=-1;
    }
    void fix(int o,int l,int r,int pos,int val){
        if(l==r){
            sor[o]+=val;
            sad[o]+=val;
            return;
        }
        if(lzy[o]!=-1) pushdown(o);
        int mid=l+r>>1;
        if(pos>mid) fix(o<<1|1,mid+1,r,pos,val);
        else fix(o<<1,l,mid,pos,val);
        sor[o]=sor[o<<1]|sor[o<<1|1];
        sad[o]=sad[o<<1]&sad[o<<1|1];
    }
    void updata(int o,int l,int r,int L,int R,int val){
        if(L<=l&&r<=R){
            sor[o]=sad[o]=lzy[o]=val;
            return;
        }
        if(lzy[o]!=-1) pushdown(o);
        int mid=l+r>>1;
        if(L>mid) updata(o<<1|1,mid+1,r,L,R,val);
        else if(R<=mid) updata(o<<1,l,mid,L,R,val);
        else {
            updata(o<<1,l,mid,L,R,val);
            updata(o<<1|1,mid+1,r,L,R,val);
        }
        sor[o]=sor[o<<1]|sor[o<<1|1];
        sad[o]=sad[o<<1]&sad[o<<1|1];
    }
    int find0(int o,int l,int r,int pos){
        if(!sor[o]) return -1;
        if(l==r) return l;
        if(lzy[o]!=-1) pushdown(o);
        int mid=l+r>>1;
        if(pos>mid) return find0(o<<1|1,mid+1,r,pos);
        else {
            int tmp=find0(o<<1,l,mid,pos);
            return tmp!=-1 ? tmp : find0(o<<1|1,mid+1,r,pos);
        }
    }
    int find1(int o,int l,int r,int pos){
        if(sad[o]==INF) return -1;
        if(l==r) return l;
        if(lzy[o]!=-1) pushdown(o);
        int mid=l+r>>1;
        if(pos>mid) return find1(o<<1|1,mid+1,r,pos);
        else {
            int tmp=find1(o<<1,l,mid,pos);
            return tmp!=-1 ? tmp : find1(o<<1|1,mid+1,r,pos);
        }
    }
    int query(int o,int l,int r,int pos){
        if(l==r) return sad[o];
        if(lzy[o]!=-1) pushdown(o);
        int mid=l+r>>1;
        if(pos>mid) return query(o<<1|1,mid+1,r,pos);
        else return query(o<<1,l,mid,pos);
    }
    inline void add(int pos,int x){
        int tmp=query(1,0,N,pos);
        if(tmp+x<=INF) fix(1,0,N,pos,x);
        else {
            fix(1,0,N,pos,x-INF-1);
            int ntp=find1(1,0,N,pos+1);
            if(ntp!=pos+1) updata(1,0,N,pos+1,ntp-1,0);
            fix(1,0,N,ntp,1);
        }
    }
    inline void del(int pos,int x){
        int tmp=query(1,0,N,pos);
        if(tmp-x>=0) fix(1,0,N,pos,-x);
        else {
            fix(1,0,N,pos,INF+1-x);
            int ntp=find0(1,0,N,pos+1);
            if(ntp!=pos+1) updata(1,0,N,pos+1,ntp-1,INF);
            fix(1,0,N,ntp,-1);
        }
    }
    int main(){
        scanf("%d",&n); int t1,t2,t3; scanf("%d %d %d",&t1,&t2,&t3);
        while(n--){
            int opt; scanf("%d",&opt);
            if(opt==1){
                int a,b,pos; scanf("%d %d",&a,&b); pos=b/30;
                if(a==0) continue;
                if(a>0){
                    add(pos,(a<<(b-pos*30))&INF);
                    add(pos+1,a>>(30-(b-pos*30)));
                } else {
                    a=-a;
                    del(pos,(a<<(b-pos*30))&INF);
                    del(pos+1,a>>(30-(b-pos*30)));
                }
            } else {
                int k,pos; scanf("%d",&k); pos=k/30;
                printf("%c
    ",(query(1,0,N,pos)&(1<<(k-pos*30)) ? 1 : 0)^'0');
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Java实现 LeetCode 382 链表随机节点
    Java实现 LeetCode 382 链表随机节点
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素
    Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
    Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
    Linux下的iwpriv(iwlist、iwconfig)的简单应用
    OCX控件的注册卸载,以及判断是否注册
    .OCX、.dll文件注册命令Regsvr32的使用
  • 原文地址:https://www.cnblogs.com/PotremZ/p/NOI2017D1T1.html
Copyright © 2011-2022 走看看