• BZOJ 4942 NOI2017 整数 (压位+线段树)


    题目大意:让你维护一个数x(x位数<=3*1e7),要支持加/减a*2^b,以及查询x的第i位在二进制下是0还是1

    作为一道noi的题,非常考验写代码综合能力,敲+调+借鉴神犇的代码 3个多小时才过...

    思路并不难,题目里b<=30n暗示压位,每次压30位可过

    先分析一下加法,加a*2^b相当于在第b-1位加a,如果进位了(即>=2^{^{30}}-1),那就在下一位+1,如果下一位还进位了,那就再下一位+1......

    暴力进位显然不可取,那么用线段树维护它,维护连续的几大位之内是否全都是1

    a<=1e9<=2^30,所以它最多被拆成两大位压进线段树里

    那么每一个大位的加法操作可以变成 (方便描述,下面的数字是从左往右读):

    1.当前位+=val,拆分出的val<=2^30

    2.如果进位了,就去线段树里找接下来连续是1的最大的位置,然后在它下一位+1 比如01011 11111 10110,就是在第三位+1(实际操作可以直接找它下一位)

    3.把这两位之间(不包括两端)的所有的大位都改成2^{^{30}}-1,区间修改打标记

    减法操作和加法非常类似,借位就是找最右边是

    查询操作非常简单没什么好说的,别忘了先下推标记再查询...

    最先我的代码写了1个多小时,维护的东西一大堆,感觉恶心得一匹而且巨难调

    实在是没想到好的优化方法,就借鉴了神犇gxzlegend的一些神级操作(orzorz):

    a组(any)维护它子节点的&值,e数组(exist)维护它子节点的|值,这个维护方法很神,比我原来想的简单得多,而且不需要乱七八糟的打标记

    find_inf和find_zero这两个函数也非常简洁明了

    以及感谢LOJ的数据顺便吐槽一句辣鸡bzoj

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <algorithm>
      4 #define ll long long 
      5 #define inf (1<<30)-1
      6 #define il inline
      7 #define ls rt<<1
      8 #define rs rt<<1|1
      9 #define N 1000100
     10 using namespace std;
     11 //re
     12 int n,ma,t1,t2,t3;
     13 int a[N<<2],e[N<<2],tag[N<<2];
     14 
     15 int gc()
     16 {
     17     int rett=0,fh=1;char p=getchar();
     18     while(p<'0'||p>'9'){if(p=='-')fh=-1;p=getchar();}
     19     while(p>='0'&&p<='9'){rett=(rett<<3)+(rett<<1)+p-'0';p=getchar();}
     20     return rett*fh;
     21 }
     22 il void pushup(int rt){
     23     e[rt]=e[rt<<1]|e[rt<<1|1];
     24     a[rt]=a[rt<<1]&a[rt<<1|1];
     25 }
     26 il void pushdown(int rt){
     27     if(tag[rt]==-1) a[ls]=a[rs]=e[ls]=e[rs]=0,tag[rt]=0,tag[ls]=tag[rs]=-1;
     28     if(tag[rt]==1) a[ls]=a[rs]=e[ls]=e[rs]=inf,tag[rt]=0,tag[ls]=tag[rs]=1;
     29 }
     30 int find_inf(int x,int l,int r,int rt)
     31 {
     32     if(a[rt]==inf) return -1;
     33     if(l==r) return l;
     34     pushdown(rt);
     35     int mid=(l+r)>>1;
     36     if(x<=mid){
     37         int pos=find_inf(x,l,mid,rt<<1);
     38         if(pos==-1) return find_inf(x,mid+1,r,rt<<1|1);
     39         else return pos;
     40     }
     41     return find_inf(x,mid+1,r,rt<<1|1);
     42 }
     43 int find_zero(int x,int l,int r,int rt)
     44 {
     45     if(!e[rt]) return -1;
     46     if(l==r) return l;
     47     pushdown(rt);
     48     int mid=(l+r)>>1;
     49     if(x<=mid){
     50         int pos=find_zero(x,l,mid,rt<<1);
     51         if(pos==-1) return find_zero(x,mid+1,r,rt<<1|1);
     52         else return pos;
     53     }
     54     return find_zero(x,mid+1,r,rt<<1|1);
     55 }
     56 int upd1(int x,int l,int r,int rt,int p)
     57 {
     58     if(l==r)
     59     {
     60         a[rt]+=p,e[rt]+=p;
     61         if(a[rt]>inf){a[rt]-=(inf+1);e[rt]-=(inf+1);return 1;}
     62         if(a[rt]<0){a[rt]+=(inf+1);e[rt]+=(inf+1);return -1;}
     63         return 0;
     64     }
     65     pushdown(rt);
     66     int mid=(l+r)>>1,ans=0;
     67     if(x<=mid) ans=upd1(x,l,mid,rt<<1,p);
     68     else ans=upd1(x,mid+1,r,rt<<1|1,p);
     69     pushup(rt);return ans;
     70 }
     71 void upd2(int L,int R,int l,int r,int rt,int val)
     72 {
     73     if(L<=l&&r<=R){
     74         tag[rt]=(val)?1:-1;
     75         a[rt]=e[rt]=(val)?inf:0;
     76         return;}
     77     pushdown(rt);
     78     int mid=(l+r)>>1;
     79     if(L<=mid) upd2(L,R,l,mid,rt<<1,val);
     80     if(R>mid) upd2(L,R,mid+1,r,rt<<1|1,val);
     81     pushup(rt);
     82 }
     83 void add(int x,int y)
     84 {
     85     int p=upd1(x,0,n,1,y);
     86     if(!p) return;
     87     int pos=find_inf(x+1,0,n,1);
     88     if(pos-1>=x+1) upd2(x+1,pos-1,0,n,1,0);
     89     upd1(pos,0,n,1,1);
     90 }
     91 void del(int x,int y)
     92 {
     93     int p=upd1(x,0,n,1,-y);
     94     if(!p) return;
     95     int pos=find_zero(x+1,0,n,1);
     96     if(pos-1>=x+1) upd2(x+1,pos-1,0,n,1,1);
     97     upd1(pos,0,n,1,-1);
     98 }
     99 int query(int x,int l,int r,int rt,int p)
    100 {
    101     if(l==r) return ((1<<p)&a[rt])?1:0;
    102     int mid=(l+r)>>1;
    103     pushdown(rt);
    104     if(x<=mid) return query(x,l,mid,rt<<1,p);
    105     else  return query(x,mid+1,r,rt<<1|1,p);
    106 }
    107 int main()
    108 {
    109     scanf("%d%d%d%d",&n,&t1,&t2,&t3);
    110     int fl,x,y;
    111     for(int i=1;i<=n;i++)
    112     {
    113         fl=gc();
    114         if(fl==1){
    115             x=gc(),y=gc();
    116             if(x>=0){
    117                 add(y/30,(x&((1<<(30-y%30))-1))<<(y%30));
    118                 add(y/30+1,x>>(30-y%30));
    119             }else{ x=-x;
    120                 del(y/30,(x&((1<<(30-y%30))-1))<<(y%30));
    121                 del(y/30+1,x>>(30-y%30));
    122             }
    123         }else{
    124             x=gc();
    125             printf("%d
    ",query(x/30,0,n,1,x%30));
    126         }
    127     }
    128     return 0;
    129 }
  • 相关阅读:
    Hibernate:组合模式解决树的映射
    以面到点的学习MFC
    linux内核--进程与线程
    控件自定义
    火车车次查询-余票查询--Api接口
    如何处理大量数据并发操作(数据库锁机制详解)
    Java单链表、双端链表、有序链表实现
    事务、数据库事务、事务隔离级别、锁的简单总结
    数据库连接池分析
    Spring面试题集
  • 原文地址:https://www.cnblogs.com/guapisolo/p/9697040.html
Copyright © 2020-2023  润新知