[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;
}
posted @ 2021-02-21 19:27  zkdxl  阅读(51)  评论(0编辑  收藏  举报