Descriptionios
Given N integers A={A[0],A[1],...,A[N-1]}. Here we have some operations:c++
Operation 1: AND opn L Rgit
Here opn, L and R are integers.優化
For L≤i≤R, we do A[i]=A[i] AND opn (here "AND" is bitwise operation).ui
Operation 2: OR opn L Rthis
Here opn, L and R are integers.spa
For L≤i≤R, we do A[i]=A[i] OR opn (here "OR" is bitwise operation).code
Operation 3: XOR opn L Rorm
Here opn, L and R are integers.blog
For L≤i≤R, we do A[i]=A[i] XOR opn (here "XOR" is bitwise operation).
Operation 4: SUM L R
We want to know the result of A[L]+A[L+1]+...+A[R].
Now can you solve this easy problem?
Input
The first line of the input contains an integer T, indicating the number of test cases. (T≤100)
Then T cases, for any case, the first line has two integers n and m (1≤n≤1,000,000, 1≤m≤100,000), indicating the number of elements in A and the number of operations.
Then one line follows n integers A[0], A[1], ..., A[n-1] (0≤A[i]<16,0≤i<n).
Then m lines, each line must be one of the 4 operations above. (0≤opn≤15)
Output
Sample Input
1 4 4 1 2 4 7 SUM 0 2 XOR 5 0 0 OR 6 0 3 SUM 0 2
Sample Output
7 18
Hint
A = [1 2 4 7]
SUM 0 2, result=1+2+4=7;
XOR 5 0 0, A=[4 2 4 7];
OR 6 0 3, A=[6 6 6 7];
SUM 0 2, result=6+6+6=18.
/*/ 題意: 給出一組數,而後有4種操做。 AND opn l r 對 l~r 段的數與 opn 進行&運算; OR opn l r 對 l~r 段的數與 opn 進行|運算; XOR opn l r 對 l~r 段的數與 opn 進行^運算; SUMl r 對 l~r 段的數求和,並輸出。 很明顯的線段樹,但是我仍是太年輕。一開始覺得只是一棵裸樹,結果寫到一半,發現不能對求和的數再進行與或非的運算,也不知道我哪裏來的勇氣,想到,既然不能對和去運算,不如把lazy全壓下去。。。MDZZ。。。 後來,隊友提示我能夠用二進制來存數,而後與或非的狀況也就變得特別簡單了。 而後就用關於二進制的線段樹來寫了這個,思路一開始是很混亂的,不過寫到後面仍是被 >> 和 << 坑了很久,仍是修行不精啊。。。 A了可是運行時間仍是比較久。 最後集訓隊隊長髮了個福利,讀入優化,速度爆炸了,又是個人代碼運行速度第一(233333)。 AC代碼: /*/
#include"algorithm" #include"iostream" #include"cstring" #include"cstdlib" #include"cstdio" #include"string" #include"vector" #include"stack" #include"queue" #include"cmath" #include"map" using namespace std; typedef long long LL ; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define FK(x) cout<<"["<<x<<"]\n" #define memset(x,y) memset(x,y,sizeof(x)) #define memcpy(x,y) memcpy(x,y,sizeof(x)) #define smallfor(T) for(int i=0 ;i<T ;i++) #define bigfor(T) for(int qq=1;qq<= T ;qq++) const int MX =1111111; const int INF=0x3f3f3f3f; int sum[MX<<2][4],lazy[MX<<2][4]; void PushUp(int rt,int i) { sum[rt][i]=sum[rt<<1][i]+sum[rt<<1|1][i]; } void PushDown(int rt,int m,int i) { if(lazy[rt][i]==0) { //若是進行了AND操做,而且該位爲0 清空下面子樹。 lazy[rt<<1][i]=0; lazy[rt<<1|1][i]=0; sum[rt<<1][i]=sum[rt<<1|1][i]=0; } if(lazy[rt][i]==1) { //若是進行了OR 操做,而且該位爲1 填滿下面子樹。 lazy[rt<<1][i]=1; lazy[rt<<1|1][i]=1; sum[rt<<1][i]=m-(m>>1); sum[rt<<1|1][i]=m>>1; } if(lazy[rt][i]==2) { //若是進行了XOR操做 if(lazy[rt<<1][i]==INF) { //若是沒有進行過任何操做,標記爲XOR操做 lazy[rt<<1][i]=2; sum[rt<<1][i]=m-(m>>1)-sum[rt<<1][i]; } else if(lazy[rt<<1][i]==2) { //若是進行過XOR操做,a^b^b==a 恢復操做內容。 lazy[rt<<1][i]=INF; sum[rt<<1][i]=m-(m>>1)-sum[rt<<1][i]; } else { //若是進行了操做而且不是XOR操做 將該操做再取XOR操做 lazy[rt<<1][i]^=1; if(lazy[rt<<1][i]==0) sum[rt<<1][i]=0; else sum[rt<<1][i]=m-(m>>1); } // 另外一棵子樹用一樣的方法處理 if(lazy[rt<<1|1][i]==INF) { lazy[rt<<1|1][i]=2; sum[rt<<1|1][i]=(m>>1)-sum[rt<<1|1][i]; } else if(lazy[rt<<1|1][i]==2) { lazy[rt<<1|1][i]=INF; sum[rt<<1|1][i]=(m>>1)-sum[rt<<1|1][i]; } else { lazy[rt<<1|1][i]^=1; if(lazy[rt<<1|1][i]==0) sum[rt<<1|1][i]=0; else sum[rt<<1|1][i]=(m>>1); } } lazy[rt][i]=INF; //標記lazy爲空 } void Build(int l,int r,int rt) { for(int i=0; i<4; i++) lazy[rt][i]=INF; //清空懶惰標記 if(r==l) { int temp; scanf("%d",&temp); // FK("temp=="<<temp); for(int i=0; i<4; i++) { sum[rt][i]=(bool)(temp&(1<<i));//【這裏必定要取(bool)不然獲得的值不會是1,而是比 1大的數】 // 該題目的方法是用sum保存每一個位上值的總數,再改變爲10進制,求和。 // 把數按照二進制保存在4個位上面 // FK(sum[rt][i]); } return; } int m=(r+l)>>1; Build(lson); Build(rson); for(int i=0; i<4; i++) PushUp(rt,i); } void UpData(int L,int R,int v,int i,int l,int r,int rt) { if(r<=R&&L<=l) { switch(v) { case 0: sum[rt][i]=0,lazy[rt][i]=v; //若是是進行AND操做,而且是0,清空和。 break; case 1: sum[rt][i]=r-l+1,lazy[rt][i]=v; //若是是進行OR 操做,而且是1,填滿和。 break; case 2: sum[rt][i]=r-l+1-sum[rt][i]; if(lazy[rt][i]==2) lazy[rt][i]=INF; else if(lazy[rt][i]==INF) lazy[rt][i]=2; else lazy[rt][i]^=1; break; default: break; } return ; } PushDown(rt,r-l+1,i); int m=(r+l)>>1; if(L<=m)UpData(L,R,v,i,lson); if(R>m) UpData(L,R,v,i,rson); PushUp(rt,i); } int Query(int L,int R,int i,int l,int r,int rt) { if(L<=l&&r<=R) { return sum[rt][i]; // 返回這個數該位的和。 } int m=(r+l)>>1; int sum=0; PushDown(rt,r-l+1,i); if(L<=m)sum+=Query(L,R,i,lson); if(R>m) sum+=Query(L,R,i,rson); return sum; } int main() { int T; scanf("%d",&T); bigfor(T) { int n,m; scanf("%d%d",&n,&m); char op[5]; Build(0,n-1,1); // FK("Build Success!"); for(int i=0; i<m; i++) { scanf("%s",op); if(op[0]=='S') { int l,r; int ans=0; scanf("%d%d",&l,&r); for(int j=0; j<4; j++) ans+=Query(l,r,j,0,n-1,1)<<j; // 將每一位的數字和用10進制進位後相加。 printf("%d\n",ans); } else { int opn,l,r; char v; scanf("%d%d%d",&opn,&l,&r); if(op[0]=='A') { //AND爲&若是某位上爲 1 那麼值不變 不然全變爲0;【區間覆蓋】 for(int j=0; j<4; j++) { int x=opn&(1<<j); // FK("j=="<<j<<" x=="<<x); if(!x)UpData(l,r,0,j,0,n-1,1); } } if(op[0]=='O') { //OR 爲|若是某位上爲 0 那麼值不變 不然全變爲1;【區間覆蓋】 for(int j=0; j<4; j++) { int x=opn&(1<<j); // FK("j=="<<j<<" x=="<<x); if(x)UpData(l,r,1,j,0,n-1,1); } } if(op[0]=='X') { //XOR爲^若是某位上爲 0 那麼值不變 不然0->1,1->0【區間更新】 for(int j=0; j<4; j++) { int x=opn&(1<<j); // FK("j=="<<j<<" x=="<<x); if(x)UpData(l,r,2,j,0,n-1,1); } } } } } return 0; }
//下面是快速讀入的福利【只適用於讀入比較多的題目,適用於題目本來複雜度爲O(n),可是本身的代碼估計會是O(nlog(n)),這個優化的做用就會比較明顯。】
//下面就是黑科技:
namespace IO { const int MT = 5e7; //1e711000kb char buf[MT]; int c, sz; void begin() { c = 0; sz = fread(buf, 1, MT, stdin); } inline bool read(int &t) { while(c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++; if(c >= sz) return false; bool flag = 0; if( buf[c] == '-')flag = 1, c++; for(t = 0; c < sz && '0' <= buf[c] && buf[c] <='9'; c++) t = t * 10 + buf[c] - '0'; if(flag) t = -t; return true; } inline bool read(char s[]) { while(c < sz && (buf[c] == ' ' || buf[c] == '\n')) c++; if(c >= sz) return false; int len = 0; while(c < sz && buf[c] != ' ' && buf[c] != '\n') s[len++] = buf[c] , c++; s[len]=0; return true; } } using namespace IO; //打開方式: int x; read(x);