题解:P11173 「CMOI R1」We Want To Run / Nilpotent
题意
对于 \(n\times n\) 的矩阵 \(A\),定义 \(f(A)\) 为最小的满足 \(A^b=O\) 的正整数 \(b\),若不存在则为 \(0\)。\(O\) 为零矩阵。给定 \(n,a\),求所有 \(n\times n\) 的值域为 \([0,a)\) 的矩阵的 \(f\) 值之和。\(1\leq n\leq 600\)。
题解
容易发现我们只关心 \(A_{i,j}\) 是否 \(>0\)。
根据经典转化,设 \(0/1\) 矩阵 \(A\) 为一张图的邻接矩阵,则 \((A^k)_{i,j}\) 表示 \(i\) 到 \(j\) 是否存在长度为 \(k\) 的路径。因此 \(f(A)\) 实际上表示其对应图的最长路,显然若图上存在环则 \(f(A)=0\),因此我们只用考虑一个 DAG。
问题转化为,定义一个 DAG 的权值为其最长路的点数 \(b\) 乘上 \((a-1)^m\),其中 \(m\) 表示其边数。求所有 DAG 的权值和。
设最长路为 \(s\to t\),考虑按照 \(s\) 到每个点 \(x\) 的最长路 \(dis\) 分层 DP。令 \(f_{i,j,k}\) 表示考虑了 \(i\) 个点,当前层有 \(j\) 个点,共有 \(k\) 层的权值和。转移需要添加第 \(k+1\) 层的点 \(S\),显然 \(S\) 合法的条件是对于每个 \(x\in S\),存在某个第 \(k\) 层的点 \(u\) 连向了 \(x\)。
考虑转移系数是什么。不妨先考虑添加一个 \(k+1\) 层的点时的贡献。若在第 \(k\) 层有 \(x\) 个点连向这个新点,则会给权值乘上 \(\dbinom{j}{x}(a-1)^{x}\),因此总的贡献是
同理,前 \(k-1\) 层的点带来的贡献是 \(a^{i-j}\)。于是连接一个新点的转移系数为 \((a^j-1)a^{i-j}\)。现在转移时枚举 \(p\) 表示添加了 \(p\) 个新点,乘上标号的方案数即可得到转移:
最终答案为 \(\sum\limits_{k=1}^nk\sum\limits_{j=1}^nf_{n,j,k}\),我们得到了 \(\mathcal{O}(n^4)\) 的做法。
考虑优化,我们发现 \(k\) 这一维不参与转移,只在最终统计答案时用到。巧妙地,我们用组合意义把这个 \(k\) 拆掉:对于每个 DAG,我们需要选择其中一层作为关键层,这样就会给层数为 \(k\) 的 DAG 乘上 \(k\) 的系数了。将 DP 状态改为 \(f_{i,j,0/1}\),其中 \(0/1\) 表示是否选择了关键层即可。时间复杂度降至 \(\mathcal{O}(n^3)\)。
:::success[代码]
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
using pii = pair<int, int>;
const int N = 605, MOD = 202407031;
template<typename T> T lowbit(T x) { return x & -x; }
template<typename T> void chk_min(T &x, T y) { x = min(x, y); }
template<typename T> void chk_max(T &x, T y) { x = max(x, y); }
template<typename T> T add(T x, T y) { return x += y, x >= MOD ? x - MOD : x; }
template<typename T> T sub(T x, T y) { return x -= y, x < 0 ? x + MOD : x; }
template<typename T> void cadd(T &x, T y) { x += y, x < MOD || (x -= MOD); }
template<typename T> void csub(T &x, T y) { x -= y, x < 0 && (x += MOD); }
int n, a, ans, f[N][N][2], C[N][N], pw[N * N];
void prework(int n) {
for (int i = 0; i <= n; ++i) for (int j = 0; j <= i; ++j)
C[i][j] = !j ? 1 : add(C[i - 1][j - 1], C[i - 1][j]);
pw[0] = 1;
for (int i = 1; i <= n * n; ++i) pw[i] = (ll)pw[i - 1] * a % MOD;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
ull x; cin >> x, a = x % MOD;
prework(n);
for (int i = 1; i <= n; ++i) f[i][i][0] = f[i][i][1] = 1;
for (int i = 1; i <= n; ++i) for (int j = 1; j <= i; ++j) {
int v0 = f[i][j][0], v1 = f[i][j][1];
int x = sub(pw[i], pw[i - j]);
for (int k = 1, p = 1; k <= n - i; ++k) {
p = (ll)p * x % MOD;
int c = (ll)p * C[i + k][k] % MOD;
cadd<int>(f[i + k][k][0], (ll)v0 * c % MOD);
cadd<int>(f[i + k][k][1], (ll)v0 * c % MOD);
cadd<int>(f[i + k][k][1], (ll)v1 * c % MOD);
}
}
for (int i = 1; i <= n; ++i) cadd(ans, f[n][i][1]);
cout << ans;
return 0;
}
:::

浙公网安备 33010602011771号