問題
https://codeforces.com/contest/1895/problem/F (diff: 2600)
が与えられる。以下の条件を満たす長さの非負整数列を数え上げよ。
- のうち少なくとも一つの要素が少なくとも一回登場する
- について
制約
解法
は次の条件を全て満たすとき、またそのときに限り、「二つ目の条件を満たすけど、一つ目の条件を満たさない数列」となる
- 二つ目の条件を満たす
- 「全ての要素が以上以下である」または「全ての要素が以上」
このことから、を以上以下の整数からなる二つ目の条件を満たす数列の数、を十分大きい数として元の問題の解がであることがわかった。
を求める方法を考える。二つの解法を挙げる。
一つ目の方法: dp
を長さの、末尾の項がであるような数列の数とすると、でこの問題を解くことができる。このdpの計算に行列累乗を使うと、で解くこともできる。
二つ目の方法: 差分の決め方を全探索
とすると、の数は通りある。この数列全てに対してそれぞれ適切なの数を数えれば良い。
は「一つ目の方法」の行列累乗を用いて計算できる。
及びの計算が難しい。そこで「二つ目の方法」の適切なについてちゃんと考えてみる。
を固定すると、、についてとしてありうる最大値が等しい。
を固定すると、としてありうる最小値はよりの方が丁度だけ大きいことが分かる。
これが全てのについて言える。
このことから、との計算は依然できていないが、であることがわかった。
以上よりを計算することに成功した。
実装
#include <bits/stdc++.h> #include "Src/Template/IOSetting.hpp" using namespace zawa; #include "atcoder/modint" using mint = atcoder::modint1000000007; using Mat = std::vector<std::vector<mint>>; Mat mult(const Mat& a, const Mat& b) { Mat res(a.size(), std::vector<mint>(b[0].size())); for (int i{} ; i < (int)a.size() ; i++) { for (int j{} ; j < (int)b[0].size() ; j++) { for (int k{} ; k < (int)a[0].size() ; k++) { res[i][j] += a[i][k] * b[k][j]; } } } return res; } Mat pow(const Mat& a, int k) { Mat res(a.size(), std::vector<mint>(a[0].size())); for (int i{} ; i < (int)a[0].size() ; i++) res[i][i] = mint::raw(1); Mat base{a}; while (k) { if (k & 1) { res = mult(res, base); } base = mult(base, base); k >>= 1; } return res; } mint solve() { int n, x, k; std::cin >> n >> x >> k; mint ans{}; if (x > 0) { Mat A(x, std::vector<mint>(x)); for (int i{} ; i < x ; i++) { for (int j{} ; j < x ; j++) { if (std::abs(i - j) <= k) { A[i][j] = mint::raw(1); } } } A = pow(A, n - 1); for (int i{} ; i < x ; i++) { for (int j{} ; j < x ; j++) { ans += A[i][j]; // std::cout << A[i][j].val() << (j + 1 == x ? '\n' : ' '); } } } ans = mint{x + k} * mint{2 * k + 1}.pow(n - 1) - ans; return ans; } int main() { SetFastIO(); int t; std::cin >> t; while (t--) { std::cout << solve().val() << '\n'; } }
感想
バチャ中はが全て少なくとも一回登場する列を数える問題と勘違いしていた。でもこの勘違いが無くても解けなかっただろう。
最初の言い換えから見つけるのが難しい。どうやったら気付けるようになれるのかなぁ。
毎回行列累乗をソラ書きしているのアホらしいのでやめたい。盆栽書かないと。