AtCoder

ABC339 F問題(Product Equality)を解く

AtCoder_ABC339_F

AtCoder が提供しているABC(AtCoder Beginner Contest)339 のF問題をC++とPythonで解いてみました。ABC339は、2024年2月3日21:00に実施されました。

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

F問題 Product Equality(Difficulty : 1716)

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

ABC339 F問題 Product Equality

大きな素数のMOD付の演算で判断します。AtCoder Problems による Difficulty は 1716 でした。

解答案

$1 \leqq i, j, k \leqq N\, (1 \leqq N \leqq 1000) $ となる $i, j, k$ に対して、$A_i \times A_j = A_k$ となる $(i, j, k)$ の組の数を求める問題です。$N$ が小さいこともあり、それほど難しくない問題と思えます。ただし、

$$1 \leqq A_i \leqq 10^{1000}$$

という制約があります。

もし、$A_i \times A_j = A_k$ が成り立っていれば、$A_i, A_j, A_k$ の剰余(mod)をとっても式は成立します。ただし逆は成立しません。

ある程度大きな素数を選んで、その剰余付き計算で、$A_i \times A_j = A_k$ となる $(i, j, k)$ の組の数を求めてみます。

これは、確率的に間違う可能性があります。紹介したプログラムでは、2つの素数を選ぶことによって、ACを取ることができました。

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

プログラムを補足します。

  • 10億から20億の間の素数2つを適当に選びます(6、7行目)。
    • 32ビット整数は、符号有りで最大約21億、符号無しで最大約42億です。符号有りの最大値に近い素数を2つ選びました。大きな素数は検索して見つけました。
  • 文字列を整数と考えて、剰余を計算する関数 convert を用意します(9ー20行目)。
  • 素数1、素数2で剰余を取った配列を c1、c2 とします。それぞれの配列の要素別の個数を map コンテナ m1、m2 に格納します(35ー40行目)。
  • p? = c?[i] × c?[j] を計算して、コンテナからその要素の個数を求めて、結果に加えます。

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

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

typedef unsigned long long int ull;

#define MOD1 1099000027
#define MOD2 1999000021

ull convert(string s, ull mod)
{
	ull result = 0;

	for (int i = 0; i < s.length(); ++i) {
		result *= 10;
		result += s[i] - '0';
		result %= mod;
	}

	return result;
}

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

	vector<ull> c1(n);
	map<ull, int> m1;
	vector<ull> c2(n);
	map<ull, int> m2;
	for (int i = 0; i < n; ++i) {
		c1[i] = convert(a[i], MOD1);
		++m1[c1[i]];
		c2[i] = convert(a[i], MOD2);
		++m2[c2[i]];
	}

	int result = 0;
	for (int i = 0; i < n; ++i) {
		for (int j = i; j < n; ++j) {
			ull p1 = (c1[i] * c1[j]) % MOD1;
			ull p2 = (c2[i] * c2[j]) % MOD2;
			int t = min(m1[p1], m2[p2]);
			if (i == j) {
				result += t;
			} else {
				result += 2 * t;
			}
		}
	}

	cout << result << endl;

	return 0;
}

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

Python プログラム例(ABC339F)

Python 版も基本的な考え方は同じです。以下となります。

"""AtCoder Beginner Contest 339 F"""
from collections import defaultdict

MOD1 = 1099000027
MOD2 = 1999000021


def convert(s, mod):
    result = 0

    for i, ch in enumerate(s):
        result *= 10
        result += ord(ch) - ord('0')
        result %= mod

    return result


n = int(input())
a = [""] * n
for i in range(n):
    a[i] = input()

c1 = [0] * n
m1 = defaultdict(int)
c2 = [0] * n
m2 = defaultdict(int)
for i in range(n):
    c1[i] = convert(a[i], MOD1)
    m1[c1[i]] += 1
    c2[i] = convert(a[i], MOD2)
    m2[c2[i]] += 1

result = 0
for i in range(n):
    for j in range(i, n):
        p1 = (c1[i] * c1[j]) % MOD1
        p2 = (c2[i] * c2[j]) % MOD2
        t = min(m1[p1], m2[p2])
        if i == j:
            result += t
        else:
            result += 2 * t

print(result)

Python は、整数の大きさに上限はありません。以下の素直な実装が Python (PyPy 3.10-v7.3.12) で AC しました。ただし、これは想定解ではなく、時間もぎりぎり間に合っているだけです。

"""AtCoder Beginner Contest 339 F"""
from collections import defaultdict

n = int(input())
a = [0] * n
for i in range(n):
    a[i] = int(input())

c = defaultdict(int)
for i in range(n):
    c[a[i]] += 1

result = 0
for i in range(n):
    for j in range(i, n):
        p = a[i] * a[j]
        if p in c:
            if i == j:
                result += c[p]
            else:
                result += 2 * c[p]

print(result)

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

最後に

与えられる整数の桁数が大きいため、コンテスト中は、Python を使って考えていました。公式解説をみて仕組みが理解できました。

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

COMMENT

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

CAPTCHA