CodeForces-617E 前綴異或和+莫隊算法

問題描述:

E. XOR and Favorite Number

Bob has a favorite number k and ai of length n. Now he asks you to answer m queries. Each query is given by a pair li and ri and asks you to count the number of pairs of integers i and j, such that l ≤ i ≤ j ≤ r and the xor of the numbers ai, ai + 1, ..., aj is equal to k.

Input

The first line of the input contains integers nm and k (1 ≤ n, m ≤ 100 000, 0 ≤ k ≤ 1 000 000) — the length of the array, the number of queries and Bob's favorite number respectively.

The second line contains n integers ai (0 ≤ ai ≤ 1 000 000) — Bob's array.

Then m lines follow. The i-th line contains integers li and ri (1 ≤ li ≤ ri ≤ n) — the parameters of the i-th query.

Output

Print m lines, answer the queries in the order they appear in the input.

 

 

解題思路:

前綴異或和:[L,R] = [0, L-1] ^ [0, R]。
在這個前提下,如果我們已知[L,R]內異或和爲k的區間數量,那麼我們可以在O(1)時間內推出L或R左移右移一格後,異或和爲k的區間的數量。
以R右移一格變成R+1爲例,此時增加的區間的右邊界一定是R+1;這時有[l, R+1] = [0,l-1]^[0,R+1] = k (l \in [L, R]), 即[0,l-1] = k^[0,R+1], 所以增加的數量是L-1~ R中前綴異或和爲k的前綴異或區間的數量。因此,只要我們保存了L-1到R的前綴異或和的數值和數量,就可以在O(1)時間內推出右邊界右移一格時異或和爲k的區間數量變化。

這時候我們就可以使用莫隊算法了。
莫隊算法是對區間進行分塊,然後以塊號順序對區間進行查詢。通常是以L/sqrt(n)作爲塊號,同一個塊內按R排序。我們可以計算一下一個塊內的時間複雜度。因爲同塊內按R排序,R只會往右移,時間複雜度爲n。而同一個塊內L的差值在sqrt(n)內,所以L會在sqrt(n)的範圍內震盪。假設塊內區間數爲m1,則最壞時間複雜度爲O(m1*sqrt(n)+n)。考慮所有區間,總的時間複雜度爲O(m*sqrt(n) + n*sqrt(n)), 即在這裏是n^(1.5)。

 

 

題解:

// 莫隊算法
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int MAX_N = 100010;
const int MAX_NUM = 1000010;

int xor_sum[MAX_N];            // 前綴異或和
int counts[2*MAX_NUM];         // 前綴異或和的值對應的數量
long long results[MAX_N];      // 查詢的解

struct Query
{
    int L, R, id, block;
}query[MAX_N];
// 先按塊排序,再按右邊界排序
bool operator<(Query& a, Query& b)
{
    if (a.block == b.block) return a.R < b.R; 
    return a.L < b.L;
}

int main()
{
    int n, m, k;
    cin >> n >> m >> k;
    memset(xor_sum, 0, sizeof(xor_sum));
    memset(counts, 0, sizeof(counts));
    int num;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &num);
        xor_sum[i] = xor_sum[i-1] ^ num; 
    }
    int s = sqrt(n);
    for (int i = 0; i < m; i++)
    {
        scanf("%d%d", &query[i].L, &query[i].R);
        query[i].id = i;
        query[i].block = query[i].L / s;
    }
    sort(query, query+m);
    int l = 1, r = 0;
    counts[xor_sum[l-1]]++;
    long long result = 0;
    // [0,L-1] ^ [0,R] = [L,R] 所以要記錄count([0,L-1] ~ [0,R])
    for (int i = 0; i < m; i++)
    {
        // 右邊界右移
        while(r < query[i].R)
        {
            r++;
            result += counts[k^xor_sum[r]];
            counts[xor_sum[r]]++;
        }
        // 右邊界左移
        while (r > query[i].R)
        {
            counts[xor_sum[r]]--;
            result -= counts[k^xor_sum[r]];
            r--;
        }
        // 左邊界右移
        while (l < query[i].L)
        {
            counts[xor_sum[l-1]]--;
            result -= counts[k^xor_sum[l-1]];
            l++;
        }
        // 左邊界左移
        while (l > query[i].L)
        {
            l--;
            result += counts[k^xor_sum[l-1]];
            counts[xor_sum[l-1]]++;
        }
        results[query[i].id] = result;
    }
    for (int i = 0; i < m; i++)
        printf("%I64d\n", results[i]);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章