1. 程式人生 > >【CodeChef】Strange Transform

【CodeChef】Strange Transform

【題目連結】

【思路要點】

  • 若我們將每一位分開考慮,異或可以看做模 2 2 意義下的加法。
  • 因此,一個位置 f k
    , x f_{k,x}
    的值可以看做從 ( k ,
    x ) (k,x)
    出發,每次可以選擇從 ( x , y
    ) (x,y)
    走到 ( x 1 , y ) , ( x , y + 1 ) (x-1,y),(x,y+1) ,最終停在 ( 0 , x )   ( f 0 , x = 1 ) (0,x)\ (f_{0,x}=1) 處的方案數對 2 2 取模。
  • 因此, f k 1 , x i = 0 k 1 k 2 f k 2 , x + i ( k 1 k 2 i )   ( M o d   2 ) f_{k_1,x}\equiv\sum_{i=0}^{k_1-k_2}f_{k_2,x+i}*\binom{k_1-k_2}{i}\ (Mod \ 2)
  • 注意到由 L u c a s Lucas 定理,當且僅當 a a 在二進位制下數位包含 b b ( a b ) % 2 = 1 \binom{a}{b}\%2=1 ,上式有特殊情況: f k , x f k 2 i , x + f k 2 i , x + 2 i   ( M o d   2 ) f_{k,x}\equiv f_{k-2^i,x}+f_{k-2^i,x+2^i}\ (Mod \ 2)
  • 現在我們來考慮一個詢問 ( k , x ) (k,x) ,由上面的推斷,我們令 k = k & ( 2 18 1 ) k=k\&(2^{18}-1) 不會使答案發生變化,下令 k = O ( N ) k=O(N)
  • k O ( N ) k≤O(\sqrt{N}) ,我們可以預處理出 f k , f_{k,*} 的結果,直接查詢。
  • k > O ( N ) k>O(\sqrt{N}) ,令 b i t bit 表示 k k 最高的為 1 1 的位置,則有 f k , x = f k 2 b i t , x f k 2 b i t , x + 2 b i t f_{k,x}=f_{k-2^{bit},x}\oplus f_{k-2^{bit},x+2^{bit}} ,遞迴求解即可。
  • 時間複雜度 O ( N N + Q N ) O(N\sqrt{N}+Q\sqrt{N})

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int MAXM = 512;
const int goal = 511;
const int Min = 262143;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, m, type;
int a[MAXM][MAXN];
int work(int pos, int k) {
	if (k == 0) return a[type][pos];
	int tmp = k & -k, ans = 0;
	ans ^= work(pos, k ^ tmp);
	if (pos + tmp <= n) ans ^= work(pos + tmp, k ^ tmp);
	return ans;
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(a[0][i]);
	for (int i = 1; i <= goal; i++)
	for (int j = 1; j <= n; j++)
		a[i][j] = a[i - 1][j] ^ a[i - 1][j + 1];
	for (int i = 1; i <= m; i++) {
		int k, x;
		read(k), read(x);
		k &= Min, type = k & goal;
		writeln(work(x, k ^ type));
	}
	return 0;
}