「清新题精讲」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\) 的转移过程可转化为矩阵乘法——
转移矩阵(即做矩阵)第 \(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;
}