AtCoder

ABC271 F問題(XOR on Grid Path)を解く

AtCoder_ABC271_F

AtCoder が提供しているABC(AtCoder Beginner Contest)271 のF問題をC++で解いてみました。ABC271は、2022年10月1日21:00に実施されました。

AtCoder の紹介はこちらに、プログラミングの方針はこちらに記事があります。

F問題 XOR on Grid Path(Difficulty : 1669)

問題はリンク先をご覧ください。

ABC271 F問題 XOR on Grid Path

正方形を対角線の左上と右下に分けて XOR を計算します。その結果を組み合わせて解を求めます。AtCoder Problems による Difficulty は、1699 でした。

解答案

制約は、$0 \leqq N \leqq 20$ です。すべての経路は、$_{40}C_{20} \doteqdot 1.3 \times 10^{11}$ 通りとなります。計算量的には、すべての経路を計算する時間はありません。

左上から出発して、右上から左下の対角線までの経路の数は、$2^{20} \doteqdot 10^6$ 通りとなります。同じ対角線から右下までの経路も同じで約 $10^6$ 通りあります。

その経路を合わせることで XOR が0となる組み合わせの数を求めることができます。

C++ プログラム例(ABC271F)

配列 sum1[i] は、左上を始点として、右上をインデックス0とする対角線にあるマスまでのXORの値とその個数を格納します(17ー40行目)。

配列 sum2[i] は、右下を始点として、右上をインデックス0とする対角線にあるマスまでのXORの値とその個数を格納します(42ー65行目)。

求めた sum1 と sum2 を合わせると、0になる組み合わせの個数が分かります(67ー73行目)。ただし、対角線の値が2回XORされているため、この値で一度XORする必要があります(70行目)。

以下が、C++プログラムです。

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

typedef long long int ll;

int main()
{
	int n;
	cin >> n;
	vector<vector<unsigned>> a(n, vector<unsigned>(n));
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j) {
			cin >> a[i][j];
		}
	}

	vector<map<unsigned, ll>> sum1(n);
	for (int i = 0; i < n; ++i) {
		vector<int> p;
		for (int j = 0; j < n - 1 - i; ++j) {
			p.push_back(0);
		}
		for (int j = n - 1 - i; j < n - 1; ++j) {
			p.push_back(1);
		}
		do {
			unsigned t = a[0][0];
			int index_j = 0;
			int index_i = 0;
			for (int j = 0; j < p.size(); ++j) {
				if (p[j] == 0) {
					++index_j;
				} else {
					++index_i;
				}
				t ^= a[index_i][index_j];
			}
			++sum1[i][t];
		} while (next_permutation(p.begin(), p.end()));
	}

	vector<map<unsigned, ll>> sum2(n);
	for (int i = 0; i < n; ++i) {
		vector<int> p;
		for (int j = 0; j < n - 1 - i; ++j) {
			p.push_back(0);
		}
		for (int j = n - 1 - i; j < n - 1; ++j) {
			p.push_back(1);
		}
		do {
			unsigned t = a[n - 1][n - 1];
			int index_i = n - 1;
			int index_j = n - 1;
			for (int j = 0; j < p.size(); ++j) {
				if (p[j] == 0) {
					--index_i;
				} else {
					--index_j;
				}
				t ^= a[index_i][index_j];
			}
			++sum2[i][t];
		} while (next_permutation(p.begin(), p.end()));
	}

	ll result = 0;
	for (int i = 0; i < n; ++i) {
		for (auto e : sum1[i]) {
			unsigned t = e.first ^ a[i][n - 1 - i];
			result += e.second * sum2[i][t];
		}
	}

	cout << result << endl;

	return 0;
}

AC(Accepted=正しいプログラム)と判定されました。

今回は、Python プログラムの紹介を省略します。

最後に

半分全列挙と呼ばれる手法です。実装は長くなりましたが、基本的な考え方は理解しやすい問題でした。

引き続き ABC の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA