题解:P11173 「CMOI R1」We Want To Run / Nilpotent
orange_new · · 题解
节选自:DP做题记录(三)(2025.4.5 - 2025.4.19)
安利一下我的博客
套用非常经典的线性代数结论,一张图的邻接矩阵的
注意到这张图中左右边权非负,那么在矩乘时,如果两个位置都不为
那么现在,
由于我们要统计方案数,因此我们考虑枚举贡献。如果这张图中最长链上的点数为
我们设
其次,这一层的点必须至少与上一层的
最后,这一层的点可以与之前层的点随便连边,之前的点一共有
我们其实可以发现,和我一样),只是在最后求答案时要乘以
现在我们可以改变 DP 的函数了,设
完整代码:
#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;
}