快轉到主要內容

c651. 三、區間xor(RXQ)

ChengHao Yang
作者
ChengHao Yang
SRE / CNCF Ambassador
目錄

題目 Problem
#

題目連結:https://zerojudge.tw/ShowProblem?problemid=c651

敘述 Description
#

給你一段 $N$ 個正整數的序列 $a_1∼a_N$ ,請你執行 $Q$ 筆操作。

輸入 Input
#

第一行有兩個正整數 $N,Q$ 。

第二行有 $N$ 個非負整數 $a_i$ 。

接下來有 $Q$ 行,每行代表一個操作。

如果是 $0\ l\ r$ ,代表詢問 $[l,r]$ 區間的每個數字做 $xor$ 運算之後的值。

如果是 $1\ x\ v$ ,代表將 $a_x$ 置換成 $v$ 。

※ $xor$ 即代表C++中的位元運算「^」。

輸出 Output
#

對於每個詢問,輸出詢問區間的每個數字做 $xor$ 運算之後的值。

範例輸入 Sample Input
#

5 3
16 9 1 5 3
0 1 5
1 1 5
0 1 5

範例輸出 Sample Output
#

30
11

提示 Hint
#

本題共有四組測試題組,條件限制如下所示。每一組可有一或多筆測試資料,該組所有測試資料皆需答對才會獲得該組分數。

cout « (16^9^1^5^3) « “\n”; 輸出即為30。

觀察一下 xor 的性質,不然這題拿不滿XD。輸入卡很緊,記得加優化。

題解 Solution
#

遇到一般的RMQ+單點修改問題,通常有幾種解題方式

  1. 線段樹 Segment Tree
  2. 二元索引樹(樹狀樹组) Binary Index Tree (Fenwick tree) 不過這題時限只有$0.5$秒 如果用前綴 $XOR$ 單點修改變成$O(N)$ 如果用線段樹遞迴查詢可能會有點慢(有實做過,只有$72%$,其他的被TLE)

這題可以使用BIT,也就是二元索引樹,但要如何使用呢? (以下把二元索引樹 簡稱 BIT)

XOR 真值表&性質
#

$A$$B$$A⊕B$
$0$$0$$0$
$1$$0$$1$
$0$$1$$1$
$1$$1$$0$

恆等律:$X⊕0=X$ 歸零律:$X⊕X=0$ 自反性:$A⊕B⊕B=A⊕(B⊕B)=A⊕0=A$

其中的歸零律自反性就是我們要使用的

拿到BIT的模板應該都是求區間和

假設我們要求$[L,R]$的區間和 是不是只要$sum(R)-sum(L-1)$?

這題改成區間 XOR BIT 初始化就改成 XOR(程式碼 Line 15) 然後把求前綴和的 $+$ 改成 XOR(程式碼 Line 9) 就變成 前綴 XOR 了

查詢只要輸出 $xxor(R)⊕xxor(L-1)$(程式碼 Line 37)

單點修改呢? 先把原本的數字跟要變成的數字做 XOR 變成 $val$(程式碼 Line 40) 利用歸零律自反性 後面需要更新的數字,統一跟 $val$ 做 XOR 單點修改就完成了

程式碼 Accepted Code
#

#include <bits/stdc++.h>
#define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define lowbit(x) ((x)&(-x))
using namespace std;
const int MAXN = 1000000+2;
int Origin[MAXN+1]={0};
int BITS[MAXN+1]={0};
int N,Q;
int xxor(int x){
	int ans=0;
	for(;x;x-=lowbit(x))
		ans^=BITS[x];
	return ans;
}
void init(int n) {
	for(int x = 1; x <= n; ++x) {
		BITS[x] = Origin[x];
		int y = x - lowbit(x);
		for(int i = x-1; i > y; i -= lowbit(i))
			BITS[x] ^= BITS[i];
	} 
}
void update(int x,int val){
	for(int i=x;i<=N;i+=lowbit(i))
		BITS[i]^=val;
}
int main(){
	_
	cin>>N>>Q;
	for(int i=1;i<=N;i++)
		cin>>Origin[i];
	init(N);
	while(Q--){
		int choice,x,y;
		cin>>choice>>x>>y;
		if(choice==0){
			cout<<(xxor(y)^xxor(x-1))<<'\n';
		}
		else{
			int OO=Origin[x]^y;
			Origin[x]=y;
			update(x,OO);
		}
	}
	return 0;
}

後記 Afterword
#

每次做到有關 XOR 的題型 都讓我想到有一題原本 時間 $O(N\log{N})$ 空間 $O(N)$ 最後複雜度可以壓在 時間 $O(N)$ 空間 $O(1)$ 下一篇就來寫這題的報告書

相關文章