[Luogu P3794] 签到题IV
\(\text{Problem}:\)题目链接
\(\text{Solution}:\)
\(\gcd\) 的性质比较常见,当以 \(i\) 为左右端点时,包含 \(a_{i}\) 的一段连续 \(\gcd\) 的值最多只会有 \(\log{a_{i}}\) 个。证明很简单,当 \(gcd\) 改变时说明减少了至少一个质因子,最小的质数是 \(2\),所以最多改变 \(\log_{2}a_{i}\) 次。
\(or\) 或者是 \(and\) 的改变次数也类似,每次改变至少将一个二进制位上的 \(0\) 变成 \(1\) 或者是把 \(1\) 变成 \(0\),故最多改变也是 \(\log_{2}a_{i}\) 次。注意 \(xor\) 没有这个性质。
对于本题来说,枚举 \(i\) 并固定 \(i\) 为左端点,向右二分可以得到 \(\log\) 段 \(\gcd\) 和 \(or\)。观察到值域较小,可以用 \(book\) 记下每段 \(\gcd\) 的左右端点,然后在 \(or\) 中直接查询,两个区间的相交长度就是这一段 \(or\) 的贡献。这样我们可以得到 \(O(n\log^2n)\) 的做法,但是它明显不够优秀。
考虑当 \(i\) 为左端点时,\(1\) 到 \(i-1\) 这一段没有任何用处,即我们对于每个 \(i\) 都是独立的。可以尝试将 \(i\) 作为右端点,那么以 \(i\) 为右端点的 \(\gcd\) 和 \(or\) 个数也是 \(\log\) 个。然后我们可以把 \(i-1\) 时的存 \(\gcd\) 和 \(or\) 的数组拿过来,和 \(a_{i}\) 分别做 \(\gcd\) 与 \(or\),去重后每个数组内不同元素个数仍然是 \(\log\) 个。
一个问题就是直接 \(\gcd\) 与 \(or\) 后元素无序了怎么办?所以我们需要证明做了如上操作后,\(\gcd\) 与 \(or\) 数组内的元素仍是有序的。
对于 \(\gcd\),存在 \(x>y\),易知 \(y|x\),那么显然有 \(\gcd(z,y)\leq \gcd(z,ky)\)。
对于 \(or\),存在 \(x>y\),那么 \(x\) \(or\) \(y=x\),那么显然有 \(y\) \(or\) \(k\) \(\leq\) \(x\) \(or\) \(k\)。
所以得到了一个 \(O(n\log n)\) 的做法。
\(\text{Code}:\)
#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
using namespace std;
const int N = 1000010;
inline int read()
{
int s = 0, w = 1;
ri char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
return s * w;
}
int n, K, a[N], v1[N], v2[N];
long long Ans;
struct Node
{
int w, len, l, r;
} p[N], q[N];
int cnt1, cnt2;
inline int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
signed main()
{
n = read(), K = read();
for (ri int i = 1; i <= n; i++)
a[i] = read();
for (ri int i = 1; i <= n; i++)
{
for (ri int j = 1; j <= cnt1; j++)
p[j].w = gcd(p[j].w, a[i]);
p[++cnt1] = (Node){a[i], 1, i, i};
int tot = 0;
for (ri int j = 1; j <= cnt1; j++)
{
if (j == 1 || p[j].w != p[j - 1].w)
tot++, p[tot] = (Node){p[j].w, p[j].len, p[tot - 1].r + 1, p[tot - 1].r + p[j].len};
else
p[tot].len += p[j].len, p[tot].r += p[j].len;
}
cnt1 = tot, tot = 0;
for (ri int j = 1; j <= cnt2; j++)
q[j].w |= a[i];
q[++cnt2] = (Node){a[i], 1, i, i};
for (ri int j = 1; j <= cnt2; j++)
{
if (j == 1 || q[j].w != q[j - 1].w)
tot++, q[tot] = (Node){q[j].w, q[j].len, q[tot - 1].r + 1, q[tot - 1].r + q[j].len};
else
q[tot].len += q[j].len, q[tot].r += q[j].len;
}
cnt2 = tot, tot = 0;
for (ri int j = 1; j <= cnt1; j++)
v1[p[j].w] = p[j].l, v2[p[j].w] = p[j].r;
for (ri int j = 1; j <= cnt2; j++)
{
int x = q[j].w ^ K;
if (!v1[x])
continue;
Ans += max(0ll, 1ll * (min(q[j].r, v2[x]) - max(q[j].l, v1[x]) + 1));
}
for (ri int j = 1; j <= cnt1; j++)
v1[p[j].w] = v2[p[j].w] = 0;
}
printf("%lld\n", Ans);
return 0;
}