ABC274Ex 题解

题意:给你一个非负整数序列 \(a\),共 \(q\) 次询问,每次询问中给出三个区间 \([l_1,r_1],[l_2,r_2],[l_3,r_3]\),判断序列 \(\{a[l_1]\oplus a[l_2],a[l_1+1]\oplus a[l_2+1],\dots,a[r_1]\oplus a[r_1+1]\}\) 是否比序列 \(a[l_3\dots r_3]\) 的字典序更小。

\(n\le 5\times 10^5,q\le 5\times 10^4,0\le a_i\le 10^{18}\)。


首先容易想到把问题转化为找出第一个不同的位置。看上去很像哈希,但是似乎没有靠谱的判断方法。

考虑一个变体问题,我们把题目中的异或改为加法。这是一个典题,只需要使用传统的多项式哈希即可解决问题。不难观察到,多项式哈希函数 \(f(S)=\sum_{i=1}^{|S|}P^{i-1}S_i\) 满足 \(f(S)+f(T)=f(S+T)\)(这里的 \(+\) 是对应位置相加),所以知道了 \(S,T\) 的哈希值就可以求出来 \(S+T\) 的。

上面的做法成立的根本原因是乘法对加法有分配律。事实上,在任意一个域下我们都可以类似地定义出一个哈希函数,而对于本题,将上面的加法改为异或,乘法改为 nim 积即可。

但是我们也可以不用一般的 nim 积:我们并不需要一个域,只需要找到一个运算 \(x\times B\),使得对于任意的 \(0\le x,y\le 10^{18}\),都有 \((x\times B)\oplus (y\times B)=(x\oplus y)\times B\)。把 \(x,y\) 看作两个 \(01\) 向量,我们让 \(B\) 是一个 \(01\) 矩阵,由于 \(\mathrm{and}\) 对 \(\oplus\) 有分配律,所以可以使用 \((\oplus,\mathrm{and})\) 矩阵乘法代替上式中的 \(\times\)。那么我们随机取一个 \(01\) 矩阵 \(B\),计算倍增哈希即可,由于向量乘矩阵可以使用位运算加速到 \(O(\log V)\),所以总复杂度为 \(O(n\log n\log V+q\log n)\)。

#include <bits/stdc++.h>

using namespace std;

const int N = 5e5 + 10, K = 60, LOGN = 20;

typedef unsigned long long ull;

int n, q;
ull a[N];
ull h[LOGN][N];
ull b[LOGN][K];

ull get(ull a, int x)
{
    return (a >> x) & 1;
}

ull mul(ull a, ull *b)
{
    ull c = 0;
    for (int i = 0; i < K; i++) 
        if (get(a, i)) c ^= b[i];
    return c;
}

void mul(ull *a, ull *b, ull *c)
{
    for (int i = 0; i < K; i++)
        for (int k = 0; k < K; k++)
            if (get(a[i], k)) c[i] ^= b[k];
}

signed main()
{
    cin.tie(0)->sync_with_stdio(0);

    cin >> n >> q;
    for (int i = 1; i <= n; i++) cin >> a[i];
    
    mt19937 rng(6);
    for (int i = 0; i < K; i++)
        for (int j = 0; j < K; j++)
            b[0][i] |= ((rng() % 2) << j);
    for (int i = 1; i < LOGN; i++)
        mul(b[i - 1], b[i - 1], b[i]);

    for (int i = 1; i <= n; i++) h[0][i] = a[i];
    for (int j = 1; (1 << j) <= n; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            h[j][i] = (h[j - 1][i] ^ mul(h[j - 1][i + (1 << j - 1)], b[j - 1]));
    
    while (q--)
    {
        int l1, r1, l2, r2, l3, r3; cin >> l1 >> r1 >> l2 >> r2 >> l3 >> r3;
        for (int i = LOGN - 1; i >= 0; i--)
            if ((max({l1, l2, l3}) + (1 << i) - 1 <= n) && (h[i][l1] ^ h[i][l2]) == h[i][l3]) l1 += (1 << i), l2 += (1 << i), l3 += (1 << i);
        if (l1 <= r1 && l3 <= r3)
        {
            if ((a[l1] ^ a[l2]) < a[l3]) cout << "Yes\n";
            else cout << "No\n";
        }
        else if (r3 - l3 <= r1 - l1) cout << "No\n";
        else cout << "Yes\n";
    }
}

posted @ 2025-02-09 10:54  user_10086  阅读(9)  评论(0)    收藏  举报