[BZOJ4942][NOI2017]整数(线段树+压位)

Posted HocRiser

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ4942][NOI2017]整数(线段树+压位)相关的知识,希望对你有一定的参考价值。

CCF的题经常是对于一个不是非常高级的算法或数据结构挖掘性质进行优化。

松爷的题总是充满常数优化气息,这个题也确实是在常数上做文章。

首先如果全加的话是可以直接暴力的,因为可以证明对每一位来说是均摊$O(1)$的,将a二进制分解一次。

然后将暴力想下去之后容易发现,二进制加法如果进位,肯定是找到这一位之前第一个为0的位加一,然后这两位之间的所有位都变成0(就是模拟竖式加法),减法反之。

于是这个东西就是前驱查找和区间修改,应用线段树解决,$O(n\\log^{2}n)$。

但是看到极其相近的数据范围就知道问题不可能这么简单。

回忆高精度的时候用什么方法优化常数:压位。这里也可以这么做。

线段树每一位存的不再是二进制数的一位了,而是60进制数。刚才的所有也全部变成“全0”和“全1”的判断。同时因为$|a|\\leq 10^9$,所以最多分解成两位。

关于线段树上求前驱后继的问题,我以前是使用自底向上再向下的方法找,很容易写错,其实可以直接自顶向下查找,因为最多查找失败一次所以复杂度是对的。

不过这题压位听上去和暴力线段树区别不大,细节却是多了几倍。

愚蠢的我只能选择抄LN神犇的代码:http://www.cnblogs.com/RabbitHu/p/UOJ314.html

  1 #include<cstdio>
  2 #include<algorithm>
  3 #define ls (x<<1)
  4 #define rs (ls|1)
  5 #define lson ls,L,mid
  6 #define rson rs,mid+1,R
  7 #define rep(i,l,r) for (int i=l; i<=r; i++)
  8 typedef long long ll;
  9 using namespace std;
 10 
 11 const int N=500010,S=60;
 12 const ll P=(1ll<<S)-1;
 13 int m,n,pos[N<<2];
 14 ll a,b,op,tag[N<<2],v[N<<2];
 15 bool all[N<<2][2];
 16 
 17 void rd(ll &x){
 18     bool t=0; x=0; char ch=getchar();
 19     while (ch<\'0\' || ch>\'9\') t|=(ch==\'-\'),ch=getchar();
 20     while (ch>=\'0\' && ch<=\'9\') x=x*10+ch-\'0\',ch=getchar();
 21     if (t) x=-x;
 22 }
 23 
 24 void put(int x,ll k){
 25     if (~pos[x]) v[pos[x]]=k;
 26     if (!k) all[x][0]=1,all[x][1]=0,tag[x]=0;
 27     else if (k==P) all[x][0]=0,all[x][1]=1,tag[x]=P;
 28         else all[x][0]=all[x][1]=0,tag[x]=-1;
 29 }
 30 
 31 void push(int x){ if (~tag[x]) put(ls,tag[x]),put(rs,tag[x]),tag[x]=-1; }
 32 void upd(int x){ all[x][0]=all[ls][0]&all[rs][0]; all[x][1]=all[ls][1]&all[rs][1]; }
 33 
 34 void build(int x,int L,int R){
 35     tag[x]=-1; all[x][0]=1; all[x][1]=0; pos[x]=L;
 36     if (L==R) return; pos[x]=-1;
 37     int mid=(L+R)>>1; build(lson); build(rson);
 38 }
 39 
 40 int find(int x,int L,int R,int pos,int k){
 41     if (all[x][!k]) return -1;
 42     if (L==R) return L;
 43     push(x); int mid=(L+R)>>1,tmp;
 44     if (pos<=mid && ~(tmp=find(lson,pos,k))) return tmp;
 45         else return find(rson,pos,k);
 46 }
 47 
 48 void mdf(int x,int L,int R,int l,int r,ll k){
 49     if (L==l && r==R){ put(x,k); return; }
 50     push(x); int mid=(L+R)>>1;
 51     if (r<=mid) mdf(lson,l,r,k);
 52     else if (l>mid) mdf(rson,l,r,k);
 53         else mdf(lson,l,mid,k),mdf(rson,mid+1,r,k);
 54     upd(x);
 55 }
 56 
 57 ll que(int x,int L,int R,int pos){
 58     if (L==R) return v[L];
 59     push(x); int mid=(L+R)>>1;
 60     if (pos<=mid) return que(lson,pos); else return que(rson,pos);
 61 }
 62 
 63 void add(int p,ll k){
 64     ll tmp=que(1,0,n,p); mdf(1,0,n,p,p,(tmp+k)&P);
 65     if (tmp+k>P){
 66         int l=find(1,0,n,p+1,0); mdf(1,0,n,l,l,v[l]+1);
 67         if (p+1<=l-1) mdf(1,0,n,p+1,l-1,0);
 68     }
 69 }
 70 
 71 void sub(int p,ll k){
 72     ll tmp=que(1,0,n,p); mdf(1,0,n,p,p,(tmp-k)&P);
 73     if (tmp-k<0){
 74         int l=find(1,0,n,p+1,1); mdf(1,0,n,l,l,v[l]-1);
 75         if (p+1<=l-1) mdf(1,0,n,p+1,l-1,P);
 76     }
 77 }
 78 
 79 int main(){
 80     freopen("bzoj4942.in","r",stdin);
 81     freopen("bzoj4942.out","w",stdout);
 82     scanf("%d%*d%*d%*d",&m); n=(m>>1)+4; build(1,0,n);
 83     while (m--){
 84         rd(op);
 85         if (op==1){
 86             rd(a); rd(b);
 87             if (!a) continue;
 88             if (a>0){
 89                 int p=b/S,q=b%S;
 90                 ll x=(a<<q)&P; if (x) add(p,x);
 91                 p++; a>>=S-q; if (b) add(p,a);
 92             }else{
 93                 a=-a; int p=b/S,q=b%S;
 94                 ll x=(a<<q)&P; if (x) sub(p,x);
 95                 p++; a>>=S-q; if (b) sub(p,a);
 96             }
 97         }else rd(a),printf("%lld\\n",(que(1,0,n,a/S)>>(a%S))&1);
 98     }
 99     return 0;
100 }

 

以上是关于[BZOJ4942][NOI2017]整数(线段树+压位)的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4942 & UOJ314:[NOI2017]整数——题解

bzoj 1493: [NOI2007]项链工厂(线段树)

noi2017 T1 整数 ——线段树

noi2017 整数 线段树or模拟

NOI2017整数

bzoj1493[NOI2007]项链工厂 线段树