题意: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 }