CF1603F October 18, 2017
考虑 \(x=0\),就是秩为 \(k\) 的矩阵个数,此时 \(n>k\) 一定无解,否则每次都会增加一个与当前向量集线性无关的向量,使秩增加 \(1\),设当前的秩为 \(i\in [0,n)\),方案数为 \(2^k-2^i\),所以答案为:
\[\prod\limits_{i=0}^{n-1}(2^k-2^i)
\]
否则 \(x\neq 0\),显然此时对于任意的 \(x\) 答案相同,将 \(x\) 加入向量集中并作为基底,统计秩为 \(r+1\) 的方案数,此时增加向量的方式有两种:
- 添加一个线性相关的向量,一共 \(n-r\) 次。设当前秩为 \(i\),那么方案数为 \(2^i\)。
- 添加一个线性无关的向量,一共 \(r\) 次。方案数为 \(2^k-2^i\),由于已经添加了 \(x\) 作为基底,\(i\in [1,r]\)。
直接写出生成函数:
\[\prod\limits_{i=1}^{r}(2^k-2^i)\cdot [x^{n-r}]\prod\limits_{i=0}^r\frac{1}{1-2^{i}x}
\]
一眼顶针,发现就是 q-binomial 的生成函数:
\[[x^{n-r}]\prod\limits_{i=0}^r\frac{1}{1-2^ix}=[x^{n-r}]x^{-r}\sum\limits_{i\ge r}\dbinom{i}{r}_2x^i=\dbinom{n}{r}_2
\]
所以:
\[\text{ans}=\sum\limits_{r=1}^n\dbinom{n}{r}_2\prod\limits_{i=1}^r(2^k-2^i)
\]
// Problem: October 18, 2017
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1603F
// Memory Limit: 500 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define eb emplace_back
#define pb pop_back
#define mt make_tuple
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int> tu;
bool Mbe;
const int N = 1e7 + 5;
const int P = 998244353;
const int i2 = (P + 1) / 2;
int n, k, x;
int fc[N], ifc[N], pw[N];
int qpow(int p, int q) {
int res = 1;
for (; q; q >>= 1, p = 1ll * p * p % P)
if (q & 1) res = 1ll * res * p % P;
return res;
}
void init(int lim) {
fc[0] = pw[0] = 1;
for (int i = 1; i <= lim; i++) {
pw[i] = 2ll * pw[i - 1] % P;
fc[i] = 1ll * fc[i - 1] * (pw[i] - 1) % P;
}
ifc[lim] = qpow(fc[lim], P - 2);
for (int i = lim - 1; ~i; i--)
ifc[i] = 1ll * ifc[i + 1] * (pw[i + 1] - 1) % P;
}
void solve() {
cin >> n >> k >> x;
if (!x) {
if (n > k) return cout << 0 << '\n', void();
int res = 1;
for (int i = 0; i < min(n, k); i++)
res = 1ll * (pw[k] - pw[i] + P) % P * res % P;
cout << res << '\n';
} else {
int res = 0, pwn = qpow(2, n);
for (int r = 1, s = 1, t = 1; r < min(n, k); r++) {
s = 1ll * (pw[k] - pw[r] + P) % P * s % P;
t = 1ll * t * (pwn - 1) % P, pwn = 1ll * pwn * i2 % P;
(res += 1ll * s * ifc[r] % P * t % P) %= P;
}
cout << (res + 1) % P << '\n';
}
}
bool Med;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cerr << (&Mbe - &Med) / 1048576.0 << " MB\n";
#ifdef FILE
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
int T = 1;
cin >> T, init(1e7);
while (T--) solve();
cerr << (int)(1e3 * clock() / CLOCKS_PER_SEC) << " ms\n";
return 0;
}
然后就做完了,复杂度 \(O(\min(n,k))\)。