Loading

「清新题精讲」Yet Another Passing-Ball Problem

Yet Another Passing-Ball Problem

\(\mathsf{\color{Thistle}{Statement}}\)

给定 \(n\) 个人和 \(k\) 个球,初始 \(i\) 号球位于 \(p_i\) 号人。每分钟,每个球可从所有者传至其余人,要求该分钟拿球者与上一分钟拿球者交集为 \(\varnothing\),且传球后每个人拿球数不得超过 \(\lfloor\frac{k}{2}\rfloor\)

\(\mathsf{\color{Thistle}{Solution}}\)

方案数只与拿球人数有关。

假设拿球人数为 \(m\) 个人,则不管谁拿,则可传给的人数为 \(n-m\)(不一定每个人都会被传求)。

观察样例解释,不难想到通过 DP 计算拆分方案,即 \(f_{i,j}\) 表示前 \(i\) 个人当前已选 \(j\) 个球的方案数,转移有:\(f_{i,j}=\sum f_{i-1,j-l}\binom{j}{l}\)(解释:第 \(i\) 个人选择 \(l\) 个球,由于不同球的编号所对应的方案不同,所以需要乘组合数)。

考虑到 \(n\) 的范围可达 \(10^7\),所以时间复杂度难以接受。不过拿球的人不超过 \(k\) 个(即最多 \(100\) 个),故考虑更改 \(f_{i,j}\) 的定义为已有 \(i\) 个人有球,选择 \(j\) 个球的方案数(转移不变)。

接下来,考虑传球的过程:令 \(g_i\) 表示当前有 \(i\) 个人有球,那么 \(g_i=\sum g_j\times f_{i,k}\times \binom{n-j}{i}\),其中 \(j\in [1,\min(k,n-i)]\),因为该分钟拿球者与上一分钟拿球者交集为 \(\varnothing\)。乘 \(f_{i,k}\) 表示球的拆分方式给 \(i\) 个有球的人,由于 \(i\) 个人为哪几个是不确定的,所以需要乘 \(\binom{n-j}{i}\)(即 \(n-j\) 个人可供选择,选出 \(i\) 个)。

至此,只需要进行上述操作 \(t\) 次即可,时间复杂度为 \(O(k^2t)\),无法通过。

考虑优化,不难发现 \(g\) 的转移过程可转化为矩阵乘法——

\[\begin{bmatrix} f_{1,k}\binom{n-1}{1} & \cdots & f_{1,k}\binom{0}{1} \\ \vdots & \ddots & \vdots \\ f_{k,1}\binom{n-1}{n} & \cdots & f_{k,k}\binom{0}{n} \end{bmatrix} \begin{bmatrix} g_{1} \\ \vdots \\ g_n \end{bmatrix} \]

转移矩阵(即做矩阵)第 \(i\) 行第 \(j\) 列为转移系数,对应相乘后为表示 \(g_j\)\(g_i\) 的贡献。

这样,通过矩阵快速幂即可得到时间复杂度为 \(O(k^3\log t)\)

\(\mathsf{\color{Thistle}{Code}}\)

只给出了核心代码,Mint 为取模模版。

const int N = 1e2 + 10, M = 1e7 + 10, mod = 1e9 + 7;

int n, k, t;
int a[N];
Mint f[N][N], g[N], fact[M], inv[M], infact[M];

void work() {
	inv[1] = fact[1] = infact[1] = fact[0] = infact[0] = 1;
	for (int i = 2; i < M; i ++)
		inv[i] = inv[mod % i] * (mod - mod / i), fact[i] = fact[i - 1] * i, infact[i] = infact[i - 1] * inv[i];
}
Mint C(int n, int m) {
	return fact[n] * infact[m] * infact[n - m];
}
struct Matrix {
	Mint a[N][N];
	Matrix() { memset(a, 0, sizeof a); }
	void init() { for (int i = 1; i < N; i ++) a[i][i] = 1; }
	Matrix operator* (Matrix &tmp) {
		Matrix res;
		for (int i = 1; i < N; i ++)
			for (int j = 1; j < N; j ++)
				for (int k = 1; k < N; k ++)
					res.a[i][j] += a[i][k] * tmp.a[k][j];
		return res;
	}
}beg, base;
Matrix operator^ (Matrix a, int b) {
	Matrix res;
	res.init();
	while (b) {
		if (b & 1) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res;
}

signed main() {
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	cin >> n >> k >> t;

	set<int> s;
	for (int i = 1; i <= k; i ++)
		cin >> a[i], s.insert(a[i]);

	f[0][0] = 1, work();
	for (int i = 1; i <= k; i ++)
		for (int j = k; j >= 1; j --)
			for (int l = 1; l <= min(j, k / 2); l ++)
				f[i][j] += f[i - 1][j - l] * C(j, l);
	
	for (int i = 1; i <= k; i ++)
		for (int j = 1; j <= min(k, n - i); j ++)
			base.a[i][j] = f[i][k] * C(n - j, i);
	beg.a[s.size()][1] = 1;

	Matrix res = (base ^ t) * beg;
	Mint ans = 0;
	for (int i = 1; i <= k; i ++)
		ans += res.a[i][1];

	cout << ans << endl;

	return 0;
}
posted @ 2024-06-11 21:59  E-Syrus  阅读(3)  评论(0编辑  收藏  举报