【C++】漸化式で生成される数列に含まれる異なる要素の個数を求める方法
問題概要
3つの整数 n、x、y、z が与えられます。これらの値をもとに、次のルールで数列を作成します。
- 数列の最初の項:x mod 231
- 2番目以降の項:ai = (a(i-1) × y + z) mod 231(ただし 1 ≤ i ≤ n - 1)
このようにして作成した数列に含まれる異なる整数の個数を求めるのが、この問題の目的です。
入出力例
たとえば、入力が n = 5、x = 1、y = 2、z = 1 の場合、出力は 5 になります。
実際に数列を計算すると、一意な値は {1, 3, 7, 15, 31} となるため、答えは 5 です。
解法の考え方
この問題は、以下の手順で解くことができます。
- MOD := 231 と定義する
- 出現済みの値を記録するための配列 temp を用意し、サイズを MOD に設定する
- p := x mod MOD とし、temp[p] を true にする
- ans := 1 で初期化する
- i が 1 から n - 1 までの間、以下を繰り返す
- p := ((p * y) + z) mod MOD を計算する
- temp[p] がすでに true の場合はループを抜ける
- そうでなければ ans を 1 増やし、temp[p] を true にする
- 最終的な ans を返す
ここで重要なのは、同じ値が一度現れた時点でループを打ち切ってよいという点です。この数列は決定的(直前の値だけで次の値が完全に決まる)なため、ある値が再び出現すると、その後は同じパターンが周期的に繰り返され、新しい異なる値は二度と現れません。
また、vector<bool> はビット単位でデータを格納するため、231 個の要素を扱ってもメモリ消費を大幅に抑えられる点も、実装上の工夫といえます。
C++による実装例
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const long long MOD = 2147483648;
int solve(int n, long long x, long long y, long long z) {
vector<bool> temp;
temp.resize(MOD);
long long p = x % MOD;
temp[p] = true;
int ans = 1;
for (int i = 1; i < n; ++i) {
p = ((p * y) + z) % MOD;
if (temp[p])
break;
++ans;
temp[p] = true;
}
return ans;
}
int main() {
cout << solve(5, 1, 2, 1) << endl;
return 0;
}入力
5, 1, 2, 1
出力
5
-
グリッド内で照らされているセルの数を求めるC++プログラム
問題の概要 ここでは、縦 h × 横 w のサイズを持つグリッドが与えられたとき、光で照らされているセルの数を求めるC++プログラムを紹介します。グリッドのセルには「電球」または「障害物」が置かれています。電球のあるセルは、そのセル自身と上下左右のセルを照らし、光は障害物に遮られない限りまっすぐ伝わっていきます。一方、障害物のあるセルは照らされることがなく、電球の光を遮って他のセルへ光が届かないようにします。電球の位置を配列 bulb、障害物の位置を配列 obstacles として受け取り、グリッド全体で照らされているセルの合計数を求めます。 たとえば、入力が h = 4、w = 4、bulb
-
【C++】グラフ内の橋(ブリッジエッジ)の数を検出するプログラムの解説
ブリッジエッジ(橋)とは? 重みなし無向グラフにおけるブリッジエッジ(橋)とは、その辺を取り除いたときにグラフが非連結(複数の連結成分に分断される)となるような辺のことです。本記事では、n個の頂点とm個の辺からなるグラフが与えられたとき、その中に含まれるブリッジの数を求めるC++プログラムを紹介します。なお、対象となるグラフには平行辺や自己ループは含まれないものとします。 問題の例 例として、n = 5、m = 6、edges = {{1, 2}, {1, 3}, {2, 3}, {2, 4}, {2, 5}, {3, 5}} という入力が与えられた場合を考えてみましょう。この場合の出力は