1. 程式人生 > >BZOJ4888 [Tjoi2017]異或和 【樹狀數組】

BZOJ4888 [Tjoi2017]異或和 【樹狀數組】

討論 樹狀數組 names php inline tps 前綴和 大於 似的

題目鏈接

BZOJ4888

題解

要求所有連續異或和,轉化為任意兩個前綴和相減
要求最後的異或和,轉化為求每一位\(1\)的出現次數
所以我們只需要對每一個\(i\)快速求出\(sum[i] - sum[j] \quad [j < i]\)當前位的\(1\)的個數
顯然是將前\(i\)個數放到某一個數據結構中查詢
我的思路到這裏停住

兩個數相減,要第\(b\)位為\(1\),我們放到具體情境中觀察:
假若\(sum[i]\)的第\(b\)位為\(1\)
....1....
....?....

1、如果\(?=0\)
就是
....1....
....0....
發現\(1\)後面的數大於等於 \(0\)

後面的數,為此時相減後該位為\(1\)的充要條件

2、如果\(?=1\)
就是
....1....
....1....
發現\(sum[i]\)\(1\)後面的數小於\(sum[j]\)\(1\)後面的數,為此時相減後該位為\(1\)的充要條件

如果第\(b\)位為\(0\)也是類似的討論

如果我們將該位為\(0\)\(1\)的數分開討論,現在問題就轉化為了,如何快速求當前某一範圍內的數的個數
顯然就是對\(0\)\(1\)分別開一個權值樹狀數組啦
題目中\(a[i]\)之和\(\le 10^6\)的條件也是一個明顯的暗示

這樣我們就\(O(20 * nlogS)\)做出這道題了

#include<algorithm>
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<map> #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define mp(a,b) make_pair<int,int>(a,b) #define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int> #define LL long long int #define lbt(x) (x & -x) using namespace std; const int maxn = 100005,maxm = 1000005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int MX = 1000001; struct BIT{ int s[maxm]; void clear(){cls(s);} void add(int u){while (u <= MX) s[u]++,u += lbt(u);} int query(int u){int re = 0; while (u) re += s[u],u -= lbt(u); return re;} int sum(int l,int r){return query(r) - query(l - 1);} }T0,T1; int n,a[maxn],mx; bool check(int b){ T0.clear(); T1.clear(); LL cnt = 0; int tmp; for (int i = 0; i <= n; i++){ tmp = a[i] % b + 1; if (a[i] & b){ cnt += T0.sum(1,tmp) + T1.sum(tmp + 1,MX); T1.add(tmp); } else { cnt += T0.sum(tmp + 1,MX) + T1.sum(1,tmp); T0.add(tmp); } } return cnt & 1; } int main(){ n = read(); int x,ans = 0; REP(i,n) a[i] = a[i - 1] + (x = read()),mx = max(mx,a[i]); for (int i = 0; mx; i++,mx >>= 1){ if (check(1 << i)) ans += (1 << i); } printf("%d\n",ans); return 0; }

BZOJ4888 [Tjoi2017]異或和 【樹狀數組】