AtCoder

ABC313 D問題(Odd or Even)を解く

AtCoder_ABC313_D

AtCoder が提供しているABC(AtCoder Beginner Contest)313 のD問題をC++とPythonで解いてみました。ABC313は、2023年8月5日21:00に実施されました。

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

D問題 Odd or Even(Difficulty : 1630)

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

ABC313 D問題 Odd or Even

XOR 演算を使うインタラクティブな問題でした。AtCoder Problems による Difficulty は 1630 でした。

解答案

K が奇数であるため、数列 A の最初の K+1 個を K+1 回の質問で決定することができます。入力例である A = (1, 0, 1, 1, 0) 、N=5、K=3 の場合の手順を説明します。

最初の4(3+1)個について以下の質問をします。ここでは、排他的論理和 XOR を ‘+’ で表現します。Ai +Ai =0、1 + 1 = 0 となります。

  • A2 + A3 + A4 = 0 (0 + 1 + 1) ※A1以外の排他的論理和
  • A1 + A3 + A4 = 1 (1 + 1 + 1) ※A2以外の排他的論理和
  • A1 + A2 + A4 = 0 (1 + 0 + 1) ※A3以外の排他的論理和
  • A1 + A2 + A3 = 0 (1 + 0 + 1) ※A4以外の排他的論理和

上記式の左辺と右辺をそれぞれ加えると以下となります。これは Ai がそれぞれ3回(K回)表れるため演算の結果では Ai がひとつになるためです。

S = A1 + A2 + A3 + A4 = 1 (0 + 1 + 0 + 0)

このため、A1 から A4 の値は、以下となります(実際に一致しています)。

  • A1 = S + A2 + A3 + A4 = 1 (1 + 0)
  • A2 = S + A1 + A3 + A4 = 0 (1 + 1)
  • A3 = S + A1 + A2 + A4 = 1 (1 + 0)
  • A4 = S + A1 + A2 + A3 = 1 (1 + 0)

残りの A5 は、例えば質問 A1 +A2 +A5 = 1 の応答と A1 と A2 の値が分かっているため、A5 = (A1 +A2 +A5) + A1 + A2 = 0 と計算ができます。

一般的に同じ手順で、A の全ての要素を求めることができます。

  • 数列 A の最初の K+1 個について、上記のように K+1 個の質問で値が分かります。
  • 残りの Nー(K+1) 個を、分かっていない値を1個だけ含めた質問により、その値が計算できます。

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

上記の手順をプログラムとして表現しました。

  • K+1 個の質問をして、その応答の和 sum を求めます(12ー22行目)。
  • 和 sum から、最初の K+1 個の値を求めます(23ー25行目)。
  • 1、2、K-1、残りの要素を組にした質問とその応答から(28ー33行目)、残りの要素の値を計算します(34ー36行目)。

以下が、C++プログラムとなります。

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

int main()
{
	int n, k;
	cin >> n >> k;
	vector<unsigned int> result(n + 1, 0);

	unsigned int sum = 0;
	vector<unsigned int> temp(k + 2, 0);
	for (int i = 1; i <= k + 1; ++i) {
		cout << "? ";
		for (int j = 1; j <= k + 1; ++j) {
			if (i != j) {
				cout << j << " ";
			}
		}
		cout << endl;
		cin >> temp[i];
		sum ^= temp[i];
	}
	for (int i = 1; i <= k + 1; ++i) {
		result[i] = sum ^ temp[i];
	}

	for (int i = k + 2; i <= n; ++i) {
		cout << "? ";
		for (int j = 1; j < k; ++j) {
			cout << j << " ";
		}
		cout << i << endl;
		cin >> result[i];
		for (int j = 1; j < k; ++j) {
			result[i] ^= result[j];
		}
	}

	cout << "! ";
	for (int i = 1; i <= n; ++i) {
		cout << result[i] << " ";
	}
	cout << endl;

	return 0;
}

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

Python プログラム例(ABC313D)

C++ のプログラムを移植しました。変数名 sum は、Python の組込み関数と名前が重複しているため、変数名を s に変更しました。また、C++ 版と異なり、質問と解答はリスト print_data に格納して、print でまとめて出力しています(12、23行目)。

"""AtCoder Beginner Contest 313 D"""
n, k = map(int, input().split())
result = [0] * (n + 1)

s = 0
temp = [0] * (k + 2)
for i in range(1, k + 2):
    print_data = []
    for j in range(1, k + 2):
        if i != j:
            print_data.append(j)
    print("?", *print_data)
    temp[i] = int(input())
    s ^= temp[i]
for i in range(1, k + 2):
    result[i] = s ^ temp[i]

for i in range(k + 2, n + 1):
    print_data = []
    for j in range(1, k):
        print_data.append(j)
    print_data.append(i)
    print("?", *print_data)
    result[i] = int(input())
    for j in range(1, k):
        result[i] ^= result[j]

print("!", *result[1:])

こちらも「AC」と判定されました。

最後に

この問題はコンテスト中には解けませんでした。公式解説が理解できたため紹介しました。結果的に難易度:青問題の初めての解説となりました。

インタラクティブな問題はテストが難しいです。入出力例の N=5、K=3 の場合と A の最後の要素を抜いた N=4、K=3 の場合に、サーバ応答を手で入力してテストしました。

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

COMMENT

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

CAPTCHA