基础线段树入门 NOI2017整数

世界如此美妙,你却如此暴躁,这样不好,不好                                                   

 ——吕秀才武林外传

题目大意
n次操作维护一个长度为n的二进制整数x,初始为丰,支持以下两种操作:
1.将这个整数加上ai · 2^{bi}

2. 询问这个整数二进制意义下第ki位的值。

n<=1000000  \left | a[i] \right |<=1e9 b<=30
题目解法

注意高位进行加法不会对低位产生影响

维护每一位的值,并在线段树上记录每个区间是否含有0或1,以便发生进退位时快速查找到进退位结束的位置。

先说 nlog^{2}n 的做法:

由于 |a| 只有 1e9 ,因此把 a分解成 ∑2^{i} 的形式,就变成了 logn 次加/减 2^{k}

考虑加法,如果这一位是0则直接改为1,否则进位,相当于这一位变成0,下一位+1。最终结果就是找到这一位下面第一个为0的位,把该位+1,[当前位,该位)变成0。

减法同理,如果是1则直接该为0,否则退位,找到这一位下面第一个为1的为,把该位-1,[当前位,该位)变成1。

使用线段树维护区间是否全为0/1,单次加减的时间复杂度就是 log⁡n ,n次 logn次加减,时间复杂度 O(nlog2n),这样只有68分。

考虑优化:维护01信息过于浪费,考虑压位,维护每连续30位的数是什么。这样一次操作最多给两个位加减,加法就判断这一位加了以后是否超过2^{30}-1 ,超过就找下面第一个不为2^{30}−1 的位,把该位+1,(当前位,该位)变成0,当前位直接变成进位后的结果。减法同理。//(合理运用二进制性质)(注意当处理+1、-1时需特判,快速找到下一个0/1减少时间复杂度到真正意义上o(nlogn))

其中维护一段区间是否全 0/全2^{30}-1  可以通过维护区间或和区间与来解决。

事实上,由于a在线段树中最多对应串个叶子结点,而叶子结点内部的进位就是整数加减法,不需要专门
在线段树上查找。我们可以直接将a 拆分成两次修改,时间复杂度o(n log n)

程序常数不怎么优秀(望给予指导)

#include<bits/stdc++.h>
using namespace std;
const int N=3000000;
const long long  base=30,all=(1<<30)-1;
long long n,opt,m,a,b,pos,zhi,tmp;
long long mark[N],val[N];
int read(){
    int x=0,f=1;
    char c=getchar();
    while((c<'0')||(c>'9')){if(c=='-')f=-1;c=getchar();}
    while((c>='0')&&(c<='9'))x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
void update(int x)
{
    if (mark[x]==1) {  mark[x*2]=1; mark[x*2+1]=1;val[x*2]=all; val[x*2+1]=all;}
    if (mark[x]==0) {  mark[x*2]=0; mark[x*2+1]=0;val[x*2]=0; val[x*2+1]=0;}
}
int  add(int a1,int l,int r,int pos,int zhi)
{
  int res=0;
    if (zhi==0) return 0;
    if (l==r) {if (all-val[a1]<zhi)  
                {val[a1]=zhi+val[a1]-all-1;  
                 if (val[a1]==0) mark[a1]=0; else mark[a1]=-1; return 1;}
           else {val[a1]=val[a1]+zhi; 
                 if (val[a1]==all) mark[a1]=1; else mark[a1]=-1; return 0;}}
   	int mid=(l+r)/2;
   	update(a1);
   	if((zhi==1)&&(mark[a1]==1)&&(pos==l)){
			mark[a1]=0;
			return 1;}
   	if (pos<=mid) { res=add(a1*2,l,mid,pos,zhi);
                    if (res) res=add(a1*2+1,mid+1,r,mid+1,1);
                  }
   	              else res=add(a1*2+1,mid+1,r,pos,zhi);
   	  mark[a1]=(mark[a1*2]==mark[a1*2+1])? mark[a1*2]:-1;
   	  return res;
   	  
 }
int  del(int a1,int l,int r,int pos,int zhi)
{    int res=0;
    if (zhi==0) return 0;
    if (l==r) {if (val[a1]<zhi)  {   val[a1]=all+1+val[a1]-zhi;   
                                     if (val[a1]==all) mark[a1]=1;
                                     else mark[a1]=-1; return 1;}
                           else  {   val[a1]=val[a1]-zhi; 
                                     if (val[a1]==0) mark[a1]=0; 
                                     else mark[a1]=-1; return 0;}}
   	int mid=(l+r)/2;
   	update(a1);
   	if((zhi==1)&&(mark[a1]==0)&&(pos==l)){
			mark[a1]=1;
			return 1;
		}
   	if (pos<=mid) { res=del(a1*2,l,mid,pos,zhi);
                    if (res) res=del(a1*2+1,mid+1,r,mid+1,1);}
   	                else res=del(a1*2+1,mid+1,r,pos,zhi);
   	  mark[a1]=(mark[a1*2]==mark[a1*2+1])? mark[a1*2]:-1;
   	  return res;
   	  
    }
long long  query(int a1,int l,int r,int pos)
{ long long res;
  if (l==r) return val[a1];
  update(a1);
  int mid=(l+r)/2;
  if (pos<=mid)  res=query(a1*2,l,mid,pos);  else res=query(a1*2+1,mid+1,r,pos);
   mark[a1]=(mark[a1*2]==mark[a1*2+1])? mark[a1*2]:-1;
   	  return res;
}
int main()
{
  n=read(); m=read(); m=read(); m=read(); m=n;
  memset(val,0,sizeof(val));
  for (int i=1;i<=n;i++)
  {  opt=read();
      if (opt==1) {a=read(); b=read();
                   pos=b/base;  tmp=b%base; 
                   if (a>0){ add(1,0,n,pos,(a&((1<<(base-tmp))-1))<<tmp);
                             add(1,0,n,pos+1,a>>(base-tmp));}
                             else
                             {  a=-a; pos=b/base;  tmp=b%base; 
                             del(1,0,n,pos,(a&((1<<(base-tmp))-1) )<<tmp);
                             del(1,0,n,pos+1,a>>(base-tmp));
                             }
                   
                  }
                  else 
                  {  b=read();
                     pos=b/base;
                      zhi=query(1,0,n,pos);
                      cout<<(zhi>>(b-pos*base)&1)<<endl;
                  }
  }
}

 

发布了26 篇原创文章 · 获赞 0 · 访问量 2730
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章