kazuma8128’s blog

競プロの面白い問題を解きます

CodeChef Strange Transform

問題概要

長さ N の数列 A がある. f(A) は i 番目の要素が A[i] XOR A[i+1] となる数列とする.
Q 個のクエリ (k, x) が与えられるので f^k(A) の x 番目の要素を求めよ.
N ≦ 2e5, Q ≦ 2e5, k ≦ 1e9, x ≦ N

解法

f^k(A)_0 がどの位置の値を XOR したものになるかを観察すると, 2冪ごとに 100....01 みたいな感じになる.
フラクタルっぽい感じ.
なので上のbitから再帰で計算してやると, 各クエリで 2^(bit count(k) ) くらいで求まるけど, それでは全然間に合わない.

よく考えると, kの上の方のbitは関係なくて, 下位18bitだけ見ればよい.
さらに, 前処理で各位置 x について k < (1<<9) で f^k(A)_x を求めておくと, 残りの上 9 bitだけの再帰になるので, 各クエリ 2^9 で間に合う.

つまり計算量 O(N sqrt N+Q sqrt N).
これ log とかで解けたりしないのかな.

ソースコード

https://www.codechef.com/viewsolution/21426207

#include <bits/stdc++.h>
using namespace std;

const int B = 18;
const int hB = B >> 1;
const int MAX = 1 << B;

int N;

int dp[1 << hB][MAX];

int calc(int i, int x) {
    if (i >= N) return 0;
    if (x < (1 << hB)) return dp[x][i];
    int t = 31 - __builtin_clz(x);
    int tx = x - (1 << t);
    return calc(i, tx) ^ calc(i + (1 << t), tx);
}

int main()
{
    ios::sync_with_stdio(false), cin.tie(0);
    int Q;
    cin >> N >> Q;
    for (int i = 0; i < N; i++) {
        cin >> dp[0][i];
    }
    for (int j = 1; j < (1 << hB); j++) {
        for (int i = 0; i < N; i++) {
            dp[j][i] = dp[j - 1][i] ^ (i + 1 < N ? dp[j - 1][i + 1] : 0);
        }
    }
    while (Q--) {
        int K, X;
        cin >> K >> X; --X;
        K &= (1 << B) - 1;
        printf("%d\n", calc(X, K));
    }
    return 0;
}