专栏文章
题解:P11173 「CMOI R1」We Want To Run / Nilpotent
P11173题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @mipl16nh
- 此快照首次捕获于
- 2025/12/03 13:44 3 个月前
- 此快照最后确认于
- 2025/12/03 13:44 3 个月前
套用非常经典的线性代数结论,一张图的邻接矩阵的 次方后每个位置 就表示 到 的所有路径中长度为 的路径条数。
注意到这张图中左右边权非负,那么在矩乘时,如果两个位置都不为 ,那么乘积也不为 ,必须至少有一个是 才可以,于是,每个位置 我们不用考虑它的具体取值,只用考虑它是否为 。
那么现在, 的含义就变成了对一个边权只有 和 的图,找到最小的 使得图中没有长度为 的链。显然,如果这张图存在环,那么图中就不存在最长链(可以一直在环上绕),因此这张图一定是一个 DAG。
由于我们要统计方案数,因此我们考虑枚举贡献。如果这张图中最长链上的点数为 ,那么说明最长链的长度为 ,于是对答案的贡献就是 。由于是 DAG 我们可以按拓扑序一层一层枚举。
我们设 表示前 个点分了 层,上一层有 个点的方案数。我们先枚举这一层的点数 ,那么这 个点就可以从这 个点中选出,因此转移时首先要乘以 。
其次,这一层的点必须至少与上一层的 个点连边,边权任意,于是转移时又要乘以 (减 是因为不能一个点都不连)。
最后,这一层的点可以与之前层的点随便连边,之前的点一共有 个,于是答案又要乘以 ,因此我们就得到了转移方程 。那么答案就是 。
我们其实可以发现, 其实顶没用的(和我一样),只是在最后求答案时要乘以 。我们考虑 的组合意义,就是在这 层中选一层作为关键层。这是因为 。
现在我们可以改变 DP 的函数了,设 表示给前 个数分层,上一层有 个数,且是否选择过关键层的答案, 到 , 到 直接用之前的转移方程。对于每一层,都可以选择这一层成为关键层,于是把 按照转移方程加到 就可以了,复杂度降低到 ,足以通过此题。
完整代码:
CPP#include <bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int N = 6e2 + 9, MOD = 202407031;
int dp[N][N][2], binom[N][N], pwr[N * N], po[N][N], n, a, ans;
int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)){
if(ch == '-')
f = -1;
ch = getchar();
}
while(isdigit(ch)){
x = ((x << 1) + (x << 3) + (ch ^ 48));
ch = getchar();
}
return x * f;
}
void write(int x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
signed main(){
n = read();
a = read();
a %= MOD;
pwr[0] = 1;
for(int i = 1; i <= n * n; i++)
pwr[i] = pwr[i - 1] * a % MOD;
for(int i = 1; i <= n; i++){
po[i][0] = 1;
for(int j = 1; j <= n; j++)
po[i][j] = po[i][j - 1] * (pwr[i] - 1 + MOD) % MOD;
}
binom[0][0] = 1;
for(int i = 1; i <= n; i++){
binom[i][0] = 1;
for(int j = 1; j <= i; j++)
binom[i][j] = (binom[i - 1][j] + binom[i - 1][j - 1]) % MOD;
}
for(int i = 1; i <= n; i++)
dp[i][i][1] = dp[i][i][0] = 1;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(dp[i][j][0] != 0){
for(int p = 1; p <= n - i; p++){
dp[i + p][p][0] = (dp[i + p][p][0] + dp[i][j][0] * binom[i + p][p] % MOD * po[j][p] % MOD * pwr[p * (i - j)] % MOD) % MOD;
dp[i + p][p][1] = (dp[i + p][p][1] + dp[i][j][0] * binom[i + p][p] % MOD * po[j][p] % MOD * pwr[p * (i - j)] % MOD) % MOD;
}
}
if(dp[i][j][1] != 0){
for(int p = 1; p <= n - i; p++)
dp[i + p][p][1] = (dp[i + p][p][1] + dp[i][j][1] * binom[i + p][p] % MOD * po[j][p] % MOD * pwr[p * (i - j)] % MOD) % MOD;
}
}
}
for(int i = 1; i <= n; i++)
ans = (ans + dp[n][i][1]) % MOD;
write(ans);
return 0;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...