題目來自洛谷網站:
暴力思路:
先進性預處理,找到每個點位置的前綴異或和,在枚舉區間。
暴力代碼:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+20;int n;
int arr[N], ls[N];//前綴異或和數組 lssigned main(){cin >> n;for(int i = 1; i <= n; i++) cin >> arr[i];//預處理-前綴異或和for(int i = 1; i <= n; i++){ls[i] = ls[i-1] ^ arr[i];}//枚舉數組int ans = 0;for(int i = 0; i < n; i++){for(int j = i+1; j <= n; j++){//得到i-j這一段的異或和ans += ls[i] ^ ls[j];}}cout << ans << endl;return 0;
}
?拆位+貢獻法優化:
①將數組A數轉換為二進制來計算,二進制中每一位計算’互不影響‘。
②題目中Ai最大為2^20,我們從20開始枚舉到0,按順序取出數組Ai的第 i 位,對數組中每個數第 i 位計算值,累加起來。
③對于每個數的第 i 為,只有1、0兩種情況,對 i 位判斷奇偶性。我們用 s 來存 i ,s 就相當于每個數第 i 位的前綴和。再用 ji 來記錄每個數中是奇數的數量,用 ou 來記錄每個數中是偶數數量。同時,ou 初始化為1,ji 初始化為0。
④如果 s 為偶數,這個區間異或為0,我們要避免這種情況。因此?s 為奇數的時,這個大的區間總共有 ou 個區間異或結果為1。s 為偶數時,總共有 ji 個區間異或結果為1。由于 i 第幾位是確定的,所以每個區間的值是確定的。對于 s 奇數,用 ans += (1 << i)* ou;對于 s 為偶數,直接ans += (1 << i)* ji。
拆位+貢獻法代碼:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+20;int n;
int arr[N];signed main(){cin >> n;for(int i = 1; i <= n; i++){cin >> arr[i];}//拆分int ans = 0;for(int i = 20; i >= 0; i--){//表示接下來枚舉元素位置j 前面有多少1、0int ji = 0, ou = 1;//表示接下來枚舉元素位置j是奇數還是偶數int s = 0;for(int j = 1; j <= n; j++){//得到arr[j]二進制“第一位” 并判斷奇數偶數int k = arr[j] >> i & 1;s += k;//奇數if(s % 2){ans += (1 << i) * ou;ji += 1;}//偶數else{ans += (1 << i) * ji;ou += 1;}}}cout << ans << endl;return 0;
}