Codeforces 1511G Chips on a Board

通过 Nim 定理,很容易发现第 $i$ 个询问的答案是 B 当且仅当 $\bigoplus\limits_{L_i \le c_j \le R_i}(c_j - L_i) = 0$。否则询问的答案就是 A

那么我们来对每一个询问计算这个异或和。发现答案 $<2^{18}$,接下来尝试根号分治,选择某一个数 $K$,用一种算法去计算最低的 $K$ 位的答案,用另一种算法去计算最高的 $18-K$ 位的答案。

两种算法都有一个共同的思想:我们会把每一个询问拆成两个询问:$(L_i, R_i)$ 会被表示为 $Q(L_i, L_i)$ 和 $Q(R_i + 1, L_i)$,这里 $Q(x, y) = \bigoplus\limits_{c_j \ge x}(c_j - y)$。其实本质就是一个差分

在改造了询问之后,对于每一个 $x \in [1, m]$,把每一个 $Q(x, y)$ 存在一些 vector 中。接着遍历 $x$,处理所有对于当前 $x$ 的询问。

怎么找到每个询问的最低 $K$ 位呢?从 $m \to 1$ 遍历 $x$,用维护当前所访问过的每一个数字 $c_i$ 的数量。某时间我们想要计算 $Q(x, y)$,就遍历每一个桶,暴力计算它们的异或和。因为我们只关心答案的末 $K$ 位,所以只需要保留 $c_i \bmod 2^K$,所以计算异或和是 $O(2^K)$ 的,这一部分的时间复杂度是 $O(n+m+2^K q)$。

怎么找到每个询问的最高 $18-K$ 位呢?可以发现,对于每一个数字 $c_i$,在我们遍历 $x$ 的过程中,$c_i - x$ 的这些高位的变化并不频繁:$c_i - x$ 的这些位的变化只有约 $\frac{m}{2^K}$ 个区间(这很好理解,因为我们不关心 $c_i - x$ 的末 $K$ 位,所以重复了 $2^K$ 次 $+1$ 后高位才会变化一次)。使用一种数据结构,支持两种操作:区间异或、单点查询。比如 BIT 就可以做到这件事。我们重新从 $m \to 1$ 遍历 $x$,当我们想要处理一个数 $c_i$ 的时候,我们找到 $c_i - y$ 的高位相同的那些段区间,然后在 BIT 中更新这些区间。当查询 $Q(x, y)$ 的时候,直接查询 BIT 中 $y$ 位置的值即可。这部分的时间复杂度是 $O(n \frac{m}{2^K} \log m + m + q)$。

记 $N = \max(n, m, q)$,在 $K$ 选定适当的情况下,可以在 $O(N \sqrt{N \log N})$ 的时间复杂度内解决这个问题。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, k = 10, K = 1 << 10;
int n, m, q, c[N], ans[N];
bool cnt[N], tub[K];
vector<pair<int, int>> vec[N]; // q[x] = {id, y}
struct binary_indexed_tree
{
    int o[N];
    void Modify(int p, int v)
    {
        for(; p <= m; p += (p & -p)) o[p] ^= v;
    }
    int Query(int p)
    {
        int res = 0;
        for(; p; p -= (p & -p)) res ^= o[p];
        return res; 
    }
} bit;
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> c[i];
        cnt[c[i]] ^= 1;
    }
    cin >> q;
    for(int i = 1; i <= q; i++)
    {
        int l, r;
        cin >> l >> r;
        vec[l].push_back(make_pair(i, l));
        vec[r + 1].push_back(make_pair(i, l));
    }
    for(int i = m; i; i--)
    {
        if(cnt[i])
        {
            tub[i % K] ^= 1;
            for(int r = i, l; r >= 1; r = l - 1)
            {
                l = max(1, r - K + 1);
                bit.Modify(l, i - l >> k); 
                bit.Modify(r + 1, i - l >> k);
            }
        }
        for(pair<int, int> qry : vec[i])
        {
            int res = bit.Query(qry.second) << k;
            for(int j = 0; j < K; j++)
                if(tub[j]) res ^= ((j - qry.second) % K + K) % K;
            ans[qry.first] ^= res;
        }
    }
    for(int i = 1; i <= q; i++) cout << "AB"[ans[i] == 0];
    return 0;
}
posted @ 2021-04-28 15:49  syksykCCC  阅读(113)  评论(0编辑  收藏  举报