題意:ios
給定一個序列,求異或和與按位與和相同的區間有幾個。算法
異或和:n個數異或起來。按位與和相似。數組
分析:ui
這纔是神題,基礎算法大雜燴。spa
問大佬這題的時候,人家只說很不難啊。。code
只能說本身太菜。blog
因爲詢問區間個數,天然要快速知道某一個區間的異或和與按位與和。string
異或和很簡單,利用他的性質,直接求前綴和便可。it
可是按位與沒有和加法和異或相似的性質,沒法直接求出。io
可是利用以前的知識能夠將全部結果預處理出來,並且只要nlogn的複雜度。
就是以前搞RMQ的ST表,很適合離線查詢。
幾乎不用動的把 min或max換成&,就能獲得O(1)查詢區間按位與和的方法。
此時,依然無從下手。
咱們還須要知道一個性質,就是說按位與是單調遞減的!
由於二進制表示中,1不會增多,只會減小,再確切一點在32位int整數中只會減小32次,最多!
因此最後只會出來相似999998877444422222221110000000這樣的值。
也就是說固定每一位左端點,從左端點按位與到最後一位都是出現想上面這樣相似的數列。
這時咱們就能發現
假設當前固定左端點爲L,第一個9的開頭是R,9的結尾是W,i爲從R到W。
異或前綴和用pre數組表示。
那麼那麼若是要是異或和和按位與相等。
必需要知足pre[i]^pre[L-1]==9
咱們須要統計的是知足這樣條件的i的數量。
可是顯然不能使用暴力的方式。
繼續利用異或的性質,a^b=c ==> a=c^b
咱們能夠知道pre[i]==9^pre[L-1]
此時咱們只要找前綴和裏有幾個是知足這個條件的。
這裏又是一個很是牛逼的方法,直接用map<int,vector<int>>記錄每個前綴異或和的全部下標。
還記得當年的有序數列計數操做麼?
upper_bound - lower_bound。。。。
最終複雜度O(n*log(ai)*log(n))
到此,全部題解都結束了,又回憶了一次,只想說好牛逼。。
絕對好題。。
代碼:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <map> 5 #include <iostream> 6 #include <vector> 7 8 9 10 using namespace std; 11 12 const int inf = 0x3f3f3f3f; 13 const int maxn = 100010; 14 15 #define fi first 16 #define se second 17 typedef long long ll; 18 typedef pair<int, int> pii; 19 20 int n; 21 int num[maxn]; 22 int pre[maxn]; 23 24 int st[maxn]; 25 int dp[maxn][20]; 26 27 map<int, vector<int> > xo; 28 29 void init() { 30 pre[0] = 0; 31 xo.clear(); 32 } 33 34 void Build_ST() { 35 st[0] = -1; 36 for(int i = 1; i <= n; i++) { 37 st[i] = ((i & (i - 1)) == 0) ? st[i - 1] + 1 : st[i - 1]; 38 dp[i][0] = num[i]; 39 } 40 for(int j = 1; j <= st[n]; j++) { 41 for(int i = 1; i + (1 << (j - 1)) <= n; i++) { 42 dp[i][j] = dp[i][j - 1] & dp[i + (1 << (j - 1))][j - 1]; 43 } 44 } 45 } 46 47 int ga(int l, int r) { 48 int k = st[r - l + 1]; 49 return dp[l][k] & dp[r - (1 << k) + 1][k]; 50 } 51 52 int gr(int zl, int l, int b) { 53 int left = l, right = n; 54 int w = l + 1; 55 while(left <= right) { 56 int mid = (left + right) >> 1; 57 int pp = ga(zl, mid); 58 if(pp == b) { 59 left = mid + 1; 60 w = mid; 61 } else { 62 right = mid - 1; 63 } 64 } 65 return w; 66 } 67 68 69 int main() { 70 // freopen("hack.in", "r", stdin); 71 // freopen("hack.out", "w", stdout); 72 scanf("%d", &n); 73 init(); 74 for(int i = 1; i <= n; i++) { 75 scanf("%d", &num[i]); 76 pre[i] = pre[i - 1] ^ num[i]; 77 xo[pre[i]].push_back(i); 78 } 79 Build_ST(); 80 ll ans = 0; 81 for(int i = 1; i <= n; i++) { 82 int w = i - 1; 83 int q = 2147483647; 84 for(int r = w; r < n; r = w) { 85 q &= num[r + 1]; 86 w = gr(i, r + 1, q); 87 int d = pre[i - 1] ^ q; 88 ans += upper_bound(xo[d].begin(), xo[d].end(), w) - lower_bound(xo[d].begin(), xo[d].end(), r + 1); 89 } 90 } 91 printf("%lld\n", ans); 92 return 0; 93 94 }