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))\)

posted @ 2024-01-18 10:17  Ender_32k  阅读(17)  评论(0编辑  收藏  举报