ここで
段目に行く通りの数
と定義します。
である時のをE_2よりも高速に求めることを考えます。
ここでkitamasa法を用いることでをで求めることができます。
また、kitamasa法ではの結果からをで求めることができるので
の結果からをで求めることができます。
このようにしてこの問題をで解くことができます。
また、の場合 である場合もを同様の方法で求めることができます。
よすぽさんのkitamasa法に関する記事
import sys
def main():
input = sys.stdin.buffer.readline
def plus_one(c0, c, k=1000, mod=998244353):
next = [0] * k
next[0] = c[k - 1] * c0[0] % mod
for i in range(1, k):
next[i] = (c[i - 1] + c[k - 1] * c0[i]) % mod
for i in range(k):
c[i] = next[i]
def double(c0, c, k=1000, mod=998244353):
mat = [0] * (k * k)
now = [i for i in c]
for i in range(k):
for j in range(k):
mat[i * k + j] = now[j]
if i < k - 1:
plus_one(c0, now, k, mod)
next = [0] * k
for m in range(k * k):
i = m // k
j = m % k
next[i] += mat[j] * mat[j * k + i] % mod
for i in range(k):
c[i] = next[i]
def kitamasa(c0, n, k=1000, mod=998244353):
actions = []
n += k - 1
while n >= 2 * k:
if n & 1 == 1:
actions.append(0)
n ^= 1
else:
actions.append(1)
n >>= 1
while n > k:
actions.append(0)
n -= 1
actions.reverse()
c = [i for i in c0]
for i in actions:
if i == 0:
plus_one(c0, c, k, mod)
else:
double(c0, c, k, mod)
return c
n, x = map(int, input().split())
a = list(map(int, input().split()))
k = 300
mod = 998244353
c0 = [0] * k
for i in range(n):
c0[k - a[i]] += 1
dp = [0] * k
c = []
is_first = True
for i in range(k):
if x - k + i < 0:
continue
if x - k + i == 0:
dp[i] = 1
continue
if is_first:
c = kitamasa(c0, x - k + i, k, mod)
is_first = False
else:
plus_one(c0, c, k, mod)
dp[i] = c[-1]
ans = 0
for m in range(k * n):
i = m // n
j = m % n
if i + a[j] >= k:
ans += dp[i]
print(ans % mod)
if __name__ == "__main__":
main()#include <bits/stdc++.h>
using namespace std;
#include <atcoder/all>
using namespace atcoder;
using ll = long long;
using mint = modint998244353; // 1000000007;
// O(K)
void plus_one(int k, const vector<mint>& c0, vector<mint>& c) {
vector<mint> next(k);
next[0] = c[k - 1] * c0[0];
for (int i = 1; i < k; i++) {
next[i] = c[i - 1] + c[k - 1] * c0[i];
}
c.swap(next);
}
// O(K^2)
void make_double(int k, const vector<mint>& c0, vector<mint>& c) {
vector<vector<mint>> cs(k);
cs[0] = c;
for (int i = 1; i < k; i++) {
cs[i] = cs[i - 1];
plus_one(k, c0, cs[i]);
}
vector<mint> next(k);
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
next[i] += cs[0][j] * cs[j][i];
}
}
c.swap(next);
}
void kitamasa(int k, const vector<mint> c0, vector<mint>& c, ll n) {
vector<bool> action;
while (n >= k * 2) {
if (n & 1) {
action.emplace_back(0);
n -= 1;
} else {
action.emplace_back(1);
n >>= 1;
}
}
while (n > k) {
n--;
action.emplace_back(0);
}
reverse(action.begin(), action.end());
for (int i = 0; i < (int)action.size(); i++) {
if (action[i]) {
make_double(k, c0, c);
} else {
plus_one(k, c0, c);
}
}
}
int main() {
ll n, x;
int k = 300;
cin >> n >> x;
assert(n <= k);
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
assert(a[i] <= k);
}
vector<mint> c(k, 0);
for (int i = 0; i < n; i++) c[k - a[i]]++;
const auto c0 = c;
// ふつうのkitamasa法を用いてるためO(K^2 log n)
// もし高速kitamasa法を用いた場合はO(K^2 + K logk logn)
vector<mint> bef(k, 0);
if (x <= k) {
bef[x - 1] = 1;
for (int i = 0; i < x - 1; i++) {
bef[x - 2 - i] = c[k - 1];
plus_one(k, c0, c);
}
} else {
kitamasa(k, c0, c, x - 1);
for (int i = 0; i < k; i++) {
bef[k - 1 - i] = c[k - 1];
plus_one(k, c0, c);
}
}
mint ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < a[i]; j++) {
ans += bef[j];
}
}
cout << ans.val() << endl;
}Bostan-Mori法でもAC出来ます。