基礎線段樹入門 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章