给定一个长度为\(n\)的序列\(a_1, a_2, \dots , a_n\)(\(1 \leq n, a_i \leq 5 \times 10^5\))求有多少对$(i, j) $知足\(1 \leq i \leq j \leq n\)且\(\gcd (a_i, a_{i + 1}, \dots , a_j) \oplus (a_i \lor a_{i + 1} \lor \dots \lor a_j) = k\)。ios
容易想到,当\(i\)不变时,随着\(j\)从小变大,区间\(\gcd\)的值只会变小,且由于每次都要除以一个质因子,因此最多变小\(\log\)次就到\(1\),同时区间或的值只会变大,且由于每次都会有一个二进制位变化,因此最多变大\(\log\)次就到最大值。
若是咱们当前知道对于每一个\(i\),全部可能出现的区间\(\gcd\)和区间或的值,就能求出\((i,j)\)的对数。
同时,咱们也能够由\(i + 1\)获得\(i\)可能出现的区间\(\gcd\)和区间或的值,因此咱们能够从\(n\)开始枚举到\(1\),直接求解便可。git
#include <iostream> #include <cstdio> #include <cctype> #include <algorithm> #define MAX_N (500000 + 5) #define SIZE (1 << 21) #define Getchar() (p1 == p2 && (p2 = (p1 = fr) + fread(fr, 1, SIZE, stdin), p1 == p2) ? EOF : *p1++) using namespace std; char fr[SIZE], * p1 = fr, * p2 = fr; void Read(int & res) { res = 0; char ch = Getchar(); while(!isdigit(ch)) ch = Getchar(); while(isdigit(ch)) res = res * 10 + ch - '0', ch = Getchar(); return; } struct Interval { int val; int r; }; int n, k; int a[MAX_N]; Interval g1[25], g2[25]; int tot1, tot2; long long ans; int gcd(int a, int b) { int c; while(b) { c = a % b; a = b; b = c; } return a; } void Update(int x, Interval g[], int & tot) { g[tot + 1].val = 0x7f7f7f7f; int d = 0; for(int i = 1; i + d <= tot; ++i) { g[i] = g[i + d]; while(g[i + d].val == g[i + d + 1].val) { g[i] = g[i + d + 1]; ++d; } } tot -= d; return; } int main() { Read(n); Read(k); for(int i = 1; i <= n; ++i) { Read(a[i]); } int t1, t2; g1[0].r = g2[0].r = n + 1; for(int i = n; i; --i) { for(int j = 1; j <= tot1; ++j) { g1[j].val = gcd(g1[j].val, a[i]); } for(int j = 1; j <= tot2; ++j) { g2[j].val |= a[i]; } g1[++tot1] = g2[++tot2] = (Interval){a[i], i}; Update(i, g1, tot1); Update(i, g2, tot2); t1 = tot1; t2 = tot2; while(t1 && t2) { if((g1[t1].val ^ g2[t2].val) == k) { ans += min(g1[t1 - 1].r, g2[t2 - 1].r) - max(g1[t1].r, g2[t2].r); } if(g1[t1 - 1].r <= g2[t2 - 1].r) --t1; else --t2; } } printf("%lld", ans); return 0; }