CF1916 Goodbye2023 H1H2 题解
前言
这场比赛打得异常痛苦。 虽然场上 H1H2 会做,但是考场上太急了导致脑抽,然后就爆掉了。。。
题意
给定
数据范围:
题解
容易想到 dp:
具体而言,分两种情况:
- 当前秩不增加,那么这行一定是前面的行向量的组合,因为前面的秩
r ,所以有2^r 种方法。 - 当前秩增加,那么这行一定不是前面的行向量的组合,因为前面的秩为
r - 1 ,所以有p^n - p^{r - 1} 中选择方法。
那么这样就得到如上转移,最终答案是
考虑如何快速求解
每次可以从
可以发现第 2 种转移一定只有
其中,走到
所以
从而可以写出
所以有
对比两边
所以就可以得到递推公式:
所以就得到了通项公式:
从而得到了快速求解
考虑将
然后这样就可以直接过 H1H2 了。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
int x = 0, f = 1;
char ch = getchar();
while (!('0' <= ch && ch <= '9')) {
if (ch == '-') f = -1; ch = getchar();
}
while ('0' <= ch && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();
}
return x * f;
}
void wr(int x) {
if (x < 0) putchar('-'), x = -x;
if (x >= 10) wr(x / 10); putchar(x % 10 + '0');
}
const int N = 5e5 + 10;
const int mod = 998244353;
int ans[N];
int add (int x) {
if (x >= mod) x -= mod; else if (x < 0) x += mod;
return x;
}
int ksm (int x, int y = mod - 2) {
int res = 1; x = add(x);
while (y) {
// cout << y << " " << res << endl;
if (y & 1) res = 1ll * res * x % mod;
x = 1ll * x * x % mod; y >>= 1;
} return res;
}
signed main() {
int n, p, k; n = rd(); p = rd(); k = rd();
int res1 = 1, res2 = 1;
for (int r = 0; r <= min(n,k); ++r) {
ans[r] = 1ll * res1 * res2 % mod;
res1 = 1ll * res1 * add(ksm(p, n) - ksm(p, r)) % mod;
res2 = 1ll * res2 * ksm(ksm(p, r + 1) - 1) % mod * add(ksm(p, n - r) - 1) % mod;
}
for (int r = 0; r <= k; ++r) printf ("%d ", ans[r]);
return 0;
}
话说,上面最后的式子是可以用找规律的?不清楚了。