[Atcoder]F - Road of the King

[Atcoder]F - Road of the King

大意

有一个人在 \(1\) 号点要进行 \(m\) 次移动,终点不必是 \(1\) 号点,假设第 \(i\) 次从 \(u\) 移动到 \(v\),那么在 \(u\)\(v\) 之间连一条有向边。

问有多少种操作序列能满足:最终 \(n\) 个点组成的图是一个强连通图。

思路

由于题目的数据范围,我们可以得知,大概是个 \(n ^ 3\) 的玩意。

但是我们需要仔细想想怎么计数?

首先,这个玩意和什么有关?

第一肯定是步数,第二是点是否走过,第三是这个点和为我们要的强连通图的关系。

然后我们考虑最难的问题,就是这个强连通图怎么判断,怎么累加答案?我们走到一个点的时候怎么判断其是否能与原来的形成一个强连通呢?还是说其目前自己和其他点成强连通,需要后续的操作去整。

不难发现,其实一个点,其强连通关系只有两种,第一种就是和起点在一个强连通里面,第二种,就是不在起点的强连通里面。

我们定义 \(f_{i, j, k}\) 表示目前已经走了 \(i\) 步,已经访问了 \(j\) 个点,与起点 \(1\) 形成的强连通的大小为 \(k\)。(以下所说的形成强连通均是与 \(1\) 形成)

考虑转移。

首先走到点,第一种情况,是没有走过,于是我们有:

\[f_{i + 1, j + 1, k} \to f_{i + 1,j + 1, k} + f_{i, j, k} \times (n - j) \]

第二种,是走过,但是没有形成强连通的点:

\[f_{i + 1, j, k} \to f_{i + 1, j, k} + f_{i, j, k} \times (j - k) \]

第三种是,走过,且已经形成了强连通:

\[f_{i + 1, j, k} \to f_{i + 1, j, k} + f_{i, j, k} * k \]

于是我们的答案就在 \(f_{m, n, n}\)

代码

#include<iostream>
using namespace std;

#define int long long

const int MAXN = 305;
const int MOD = 1e9 + 7;

//f[i][j][k] 走了 i 步,访问了 j 个点,以 1 为根的 scc 大小为 k
int f[MAXN][MAXN][MAXN];

int n, m;

signed main(){
	freopen("rotk.in", "r", stdin);
	freopen("rotk.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	
	cin >> n >> m;
	
	f[0][1][1] = 1;
	
	for(int i = 0;i <= m - 1;i ++){
		for(int j = 1;j <= n;j ++){
			for(int k = 1;k <= n;k ++){
				if(!f[i][j][k]) continue;
				//没走的
				f[i + 1][j + 1][k] += f[i][j][k] * (n - j) % MOD;
				f[i + 1][j + 1][k] %= MOD;
				//走过,但不在 1 的scc
				f[i + 1][j][k] += f[i][j][k] * (j - k) % MOD;
				f[i + 1][j][k] %= MOD;
				//走过,在 1 的 scc
				f[i + 1][j][j] += f[i][j][k] * k % MOD;
				f[i + 1][j][j] %= MOD;
			}
		}
	}
	
	cout << f[m][n][n] % MOD << '\n';
	return 0;
}

posted @ 2025-12-14 21:52  To_Carpe_Diem  阅读(8)  评论(0)    收藏  举报