半分全列挙した!
問題概要
正の整数 と整数 が与えられる。以下の条件を満たす正の整数 の組の個数を求めよ。
制約
考えたこと
愚直な方法としては、次のように 4 重ループをする解法が考えられるかもしれない。しかしこれでは の計算量を要する。
long long res = 0; for (int a = 1; a <= N; ++a) { for (int b = 1; b <= N; ++b) { for (int c = 1; c <= N; ++c) { for (int d = 1; d <= N; ++d) { if (a + b - c - d == K) ++res; } } } }
よって、工夫が必要になる。このような場面でしばしば有効なのが半分全列挙!!!具体的には、
- := となるような の個数
- := となるような の個数
として、次のようにすれば OK。なお、 と とで答えは変わらないので、 の場合は としている ( を満たすものとしている)。
long long res = 0; for (int v = K; v <= N*2; ++v) { res += P[v] * Q[v-K]; }
よって、配列 さえ求められれば、 で答えが出せるのだ!!!
なお、配列 と はまったく同じものなので、配列 のみ求める。
配列 P を求める
残る問題は
- := となるような の個数
を求めることである。試しに の場合を表にすると、次のようになる。
a \ b | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 |
2 | 3 | 4 | 5 | 6 | 7 |
3 | 4 | 5 | 6 | 7 | 8 |
4 | 5 | 6 | 7 | 8 | 9 |
5 | 6 | 7 | 8 | 9 | 10 |
これをみると、
- のときは、 個
- となる は 1 個
- となる は 2 個
- となる は 3 個
- となる は 4 個
- となる は 5 個
- のときは、 個
- となる は 4 個
- となる は 3 個
- となる は 2 個
- となる は 1 個
ということがわかる。より簡潔に書くならば、
と書ける。よって配列 を求めるのも の計算量でできる。
コード
全体の計算量も となる。
#include <bits/stdc++.h> using namespace std; int main() { long long N, K; cin >> N >> K; if (K < 0) K = -K; vector<long long> num(N*2+1, 0); for (long long n = 2; n <= N*2; ++n) num[n] = min(n-1, N*2+1-n); long long res = 0; for (long long n = K; n <= N*2; ++n) res += num[n] * num[n-K]; cout << res << endl; }