1. 程式人生 > 實用技巧 >巧妙拆分每位的位運算區間查詢題。題目見內容。

巧妙拆分每位的位運算區間查詢題。題目見內容。

題目:定義運算x×y=~(x&y),x,y為無符號整數型別。
給一無符號整數陣列a[],支援2個操作:

操作1:給出整數l,r和無符號整數q,輸出q×a[l]×a[l+1]×……×a[r]。
操作2:給出整數p和無符號整數x將a[p]修改為x。

輸入:第一行:n,m<=10^5,n為陣列大小,m為操作個數。

第二行:n個無符號整數。

第3行至m+2行:操作:如:1 l r q或2 p x

輸出:每個操作1運算得到的答案並換行。

樣例輸入:

5 5
571 342 228 152 192
1 1 5 409
2 1 414
1 1 2 100
2 4 341
1 2 5 315

樣例輸出:

4294967103
4294966957
4294967103

分析:區間查詢修改=>線段樹解決。但是很顯然該運算是不可使用結合律的,如a×b×c≠a×(b×c),寫出來就知道了:

a×b×c=~(~(a&b)&c)=a&b|~ca×(b×c)=~(a&(~(b&c)))=~a|(b&c)顯然不等。因此常規的線段樹做法就做不出來了。因為該運算一定要按順序來。

問題出在我們只能預處理出線段樹的區間,而進行運算時第一個數又不線上段樹裡。如何解決?假如我們把查詢的數q與該區間第一個數運算得到tmp,而線段樹裡面儲存了第一個數為tmp時的運算結果,那麼這道題就做完了。

但tmp在2^32-1範圍,沒有時間也沒有空間進行預處理。注意!這是位運算。

而顯然位運算每一位都是獨立的,我們把查詢的數拆成32位二進位制數,按照上面的思路(即第一個數為0/1)求出每一位運算後的結果,那麼這個問題就可以完美解決了。即線段樹每個節點有引數mul[i][j],0<=i<32,j=0/1,表示對於二進位制第i位,該節點區間第一個數為j運算後的結果。

接下來就是如何預處理引數mul[i][j]和單點修改了。關係比較簡單就不再贅述。

注意葉子節點mul[i][j]=j。

貼上程式碼:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e5+10
; typedef unsigned int uint; int n,m,k; uint now;//全域性變數存now進行區間運算。 uint a[maxn]; struct seg{ int l,r; uint mul[32][2]; }t[maxn<<2]; //mul[i][0]表示該區間第i位二進位制數,區間左端點為0時整個區間運算結果。 //同理mul[i][1]表示該區間第i位二進位制數,區間左端點為1時整個區間運算結果。 //這2個用於合併區間。 int cal(int x,int y){if(x==y&&x==1)return 0;return 1;}//nand運算。 void update(int id,int j)//更新 { int ls=id<<1,rs=ls+1; t[id].mul[j][0]=t[rs].mul[j][cal(t[ls].mul[j][0],(a[t[rs].l]&(1<<j))>>j)]; t[id].mul[j][1]=t[rs].mul[j][cal(t[ls].mul[j][1],(a[t[rs].l]&(1<<j))>>j)];//運算。 } void query(int id,int l,int r,int p)//對l-r區間的第p位進行運算。 { if(t[id].r<l||t[id].l>r)return ; if(t[id].l>=l&&t[id].r<=r) { now=t[id].mul[p][cal(now,(a[t[id].l]&(1<<p))>>p)];return; }query(id<<1,l,r,p); query((id<<1)+1,l,r,p); } void build(int id,int l,int r) { t[id].l=l;t[id].r=r; if(l==r) { for(int j=0;j<32;j++) { t[id].mul[j][0]=0; t[id].mul[j][1]=1;//初始化。 }return ; }int mid=(l+r)/2; build(id<<1,l,mid);build((id<<1)+1,mid+1,r); for(int j=0;j<32;j++)update(id,j); } void change(int id,int p,uint x) { if(t[id].l==t[id].r)return ; int mid=(t[id].l+t[id].r)/2; if(p<=mid)change(id<<1,p,x); else change((id<<1)+1,p,x); for(int j=0;j<32;j++)update(id,j); } int main() { //freopen("nand.in","r",stdin); //freopen("nand.out","w",stdout); scanf("%d%d",&n,&m); int i; for(i=1;i<=n;i++)scanf("%u",&a[i]); build(1,1,n); int opt,l,r,j; uint x; for(i=1;i<=m;i++) { scanf("%d",&opt); if(opt==1){ scanf("%d%d%u",&l,&r,&x); uint ans=0; for(j=0;j<32;j++) { now=(x&(1<<j))>>j; query(1,l,r,j); ans+=now<<j; }printf("%u\n",ans); }else { scanf("%d%u",&l,&x); a[l]=x; change(1,l,x); } }return 0; }