C++で解く:行の最小値と列の最大値を保持するシーケンス(A, B)のペア数を求めるアルゴリズム
この記事では、N×M のグリッドに対して特定の条件を満たすシーケンスのペア (A, B) の総数を求める問題を、C++ を使って効率的に解く方法を解説します。
問題の概要
3つの整数 N、M、K が与えられます。ここで、N 個の行(横方向)と M 個の列(縦方向)からなるグリッドを考えます。各セルには 1 以上 K 以下の整数を書き込むこととし、これをもとにシーケンス A と B を次のように定義します。
1 から N までの各 i について、A[i] は i 番目の行に含まれるすべての要素の最小値
1 から M までの各 j について、B[j] は j 番目の列に含まれるすべての要素の最大値
このとき、条件を満たすペア (A, B) の総数を求めます。答えが非常に大きくなる可能性があるため、結果は 998244353 で割った余りとして返します。
入力例と出力例
たとえば、N = 2、M = 2、K = 2 の場合、出力は 7 になります。これは、(A[1], A[2], B[1], B[2]) の組み合わせが以下の7通りになるためです。
- (1,1,1,1)、(1,1,1,2)、(1,1,2,1)、(1,1,2,2)
- (1,2,2,2)、(2,1,2,2)、(2,2,2,2)
解法のアプローチ
この問題は、各行の最小値がちょうど t になるような場合の数を、t = 1 から K まで順に足し合わせることで求められます。具体的な手順は以下の通りです。
p := 998244353
関数 power(a, b) を定義し、(a^b) mod p を返すようにする
メイン処理では以下を実行する:
n が 1 の場合:
power(K, m) を返す
m が 1 の場合:
power(K, n) を返す
ans := 0
t を 1 から K まで 1 ずつ増やしながら繰り返す:
ans := (ans + (power(t, n) - power(t - 1, n) + p) mod p * power(K - t + 1, m)) mod p
ans を返す
考え方のポイント
power(t, n) - power(t-1, n) の部分は、「n 個の要素がすべて t 以下であり、かつ少なくとも1つがちょうど t である」ような構成の数、つまり行の最小値がちょうど t となる場合の数を表しています。一方、power(K - t + 1, m) は、その条件のもとで列の最大値として許容される B の構成数に対応します。これらを掛け合わせて t ごとに合計することで、全体のペア数が得られます。
C++による実装例
それでは、実際のコードを見てみましょう。
#include <bits/stdc++.h>
using namespace std;
long p = 998244353;
long power(long a, long b, long ret = 1){
for (; b; b >>= 1, a = a * a % p)
if (b & 1)
ret = ret * a % p;
return ret;
}
long solve(int n, int m, int K){
if (n == 1)
return power(K, m);
if (m == 1)
return power(K, n);
long ans = 0;
for (long t = 1; t <= K; t++){
ans = (ans + (power(t, n) - power(t - 1, n) + p) % p * power(K - t + 1, m)) % p;
}
return ans;
}
int main(){
int N = 2;
int M = 2;
int K = 2;
cout << solve(N, M, K) << endl;
}
実行結果
入力
2, 2, 2
出力
7
まとめ
本記事では、グリッドの行最小値・列最大値からなるシーケンスのペア数を数える問題を取り上げました。累乗計算を高速に行う power() 関数(繰り返し二乗法)を活用し、最小値ごとに場合を分けて合計するという方針により、計算量 O(K log N) で効率よく解答できます。競技プログラミングでも頻出の「mod 998244353」での剰余計算の扱い方も参考になるでしょう。
-
C++で二分木の最大値(または最小値)を求める方法
この記事では、二分木が与えられたときに、その中から最大値(または最小値)を持つノードを見つける方法を解説します。 問題の概要 与えられた二分木の中から、最大値および最小値を持つノードの値を求めるのが課題です。 入力例 出力例 max = 9 , min = 1 解法のアプローチ 二分木の最大値を求めるには、木全体を走査する必要があります。基本的な考え方は次のとおりです。 ルートノードから出発し、再帰的に左部分木と右部分木を走査します。 各ノードにおいて、そのノードの値・左部分木の最大値・右部分木の最大値を比較します。 最も大きい値を現在の最大値として返し、再帰的に結果を親ノードへ伝えてい
-
C#で配列内の最大要素と最小要素を見つける方法
C#で配列の中から最大値と最小値を求めるには、まず配列の最初の要素を最大値・最小値の初期値として設定し、残りの要素と順番に比較していくのが基本的なアプローチです。 考え方 変数 max と min に、それぞれ配列の先頭要素(arr[0])を代入しておきます。その後、2番目以降の要素を1つずつ取り出しながら、以下のように比較を行います。 最大値を求める場合 現在の要素が max より大きければ、その値で max を更新します。 max) { max = arr[i]; } 最小値を求める場合 現在の要素が min より小さければ、その値で min を更新します。 if(arr[i]