ACM: FZU 2105 Digits Count - 位運算的線段樹【黑科技福利】

  FZU 2105  Digits Count
Time Limit:10000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u

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

For each test case and for each "SUM" operation, please output the result with a single line.

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);
相關文章
相關標籤/搜索