AND と XOR は、それぞれ「掛け算」と「足し算」に対応するので、行列累乗が使える!
問題概要
個の整数の組
と
が与えられる。
数列 は次の漸化式で定義される。
(
)
AND
XOR
AND
XOR ... XOR
AND
(
)
の値を求めよ。
制約
考えたこと
AND は掛け算、XOR は足し算とみなせる。実際、各桁ごとにみると、次のようになっている。
- AND は、mod 2 世界での掛け算になっている
- XOR は、mod 2 世界での足し算になっている (mod 2 では 1 + 1 = 0 になる)
AND | 0 | 1 |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
XOR | 0 | 1 |
---|---|---|
0 | 0 | 1 |
1 | 1 | 0 |
よって、掛け算と足し算の記号を使うと、漸化式は
となる。これはごく普通の漸化式だ。漸化式の第 項は行列累乗によって効率的に求められることが知られている。
この漸化式は、次のように行列で表せる。ここで、行列演算における「掛け算」の部分は AND、「足し算」の部分は XOR を表しているものとする。
よって、
としたとき、 を計算することで、
も求められる。計算量は
となる。
コード
XOR 演算の単位元は、通常の足し算と同じく「0」だが、AND 演算の単位減は、通常の掛け算とは異なり、「111...1 (二進法)」という数であることに注意して実装する。
単位行列も、いつものように対角成分に 1 が並ぶのではなく、111...1 (二進法) を並べる。
#include <bits/stdc++.h> using namespace std; // AND 演算の単位元 (普通の掛け算の 1 に相当) const long long UNIT = (1LL<<50) - 1; template<class T> struct Matrix { vector<vector<T> > val; Matrix(int n, int m, T x = 0) : val(n, vector<T>(m, x)) {} void init(int n, int m, T x = 0) {val.assign(n, vector<T>(m, x));} size_t size() const {return val.size();} vector<T>& operator [] (int i) {return val[i];} }; template<class T> Matrix<T> operator * (Matrix<T> A, Matrix<T> B) { Matrix<T> R(A.size(), B[0].size()); for (int i = 0; i < A.size(); ++i) for (int j = 0; j < B[0].size(); ++j) for (int k = 0; k < B.size(); ++k) R[i][j] ^= (A[i][k] & B[k][j]); return R; } template<class T> vector<T> operator * (Matrix<T> A, vector<T> B) { vector<T> v(A.size()); for (int i = 0; i < A.size(); ++i) for (int k = 0; k < B.size(); ++k) v[i] ^= (A[i][k] & B[k]); return v; } template<class T> Matrix<T> pow(Matrix<T> A, long long n) { Matrix<T> R(A.size(), A.size()); for (int i = 0; i < A.size(); ++i) R[i][i] = UNIT; while (n > 0) { if (n & 1) R = R * A; A = A * A; n >>= 1; } return R; } int main() { long long K, M; cin >> K >> M; --M; vector<long long> A(K), C(K); for (int i = 0; i < K; ++i) cin >> A[i]; for (int i = 0; i < K; ++i) cin >> C[i]; if (M < K) { cout << A[M] << endl; return 0; } Matrix<long long> mat(K, K, 0); for (int i = 0; i < K; ++i) { mat[K-1][i] = C[K-1-i]; if (i < K-1) mat[i][i+1] = UNIT; } vector<long long> res = pow(mat, M) * A; cout << res[0] << endl; }
別解
きたまさ法や、Bostan-Mori 法も適用できる。
多項式演算を愚直に実装した場合、これらの解法は となる。