BZOJ传送门
题目描述
在 Byteland 一共有 n n n 个城市,编号依次为 1 1 1 到 n n n,它们之间计划修建 n ( n − 1 ) 2 \frac{n(n−1)}{2} 2n(n−1)条单向道路,对于任 意两个不同的点 i i i 和 j j j,在它们之间有且仅有一条单向道路,方向要么是 i i i 到 j j j,要么是 j j j 到 i i i。换句话 说,这是一个 n n n 个点的竞赛图。
Byteasar 居住在 1 1 1 号城市,他希望从 1 1 1 号城市出发,沿着单向道路不重复地访问一些城市,使得访 问的城市数尽可能多。
请写一个程序,帮助 Byteasar 计算有多少种道路修建方式,使得从 1 1 1 号点出发的最长简单路径经过点数恰好为 k k k,由于答案可能很大,请对 P P P 取模输出。
输入输出格式
输入格式
第一行包含两个正整数 n , P n,P n,P,表示点数和模数。
输出
输出 n n n 行,第 i i i 行输出从 1 1 1 出发的最长简单路径经过点数恰好为 i i i 的竞赛图个数模 P P P。
输入输出样例
输入样例#1:
2 233
输出样例#1:
1
1
输入样例#2:
3 233
输出样例#3:
2
2
4
解题分析
前六个点, 没啥好说的, 暴力打表即可 A C AC AC。
然后竞赛图有个很好的性质:一定存在一条哈密尔顿路径。
所以我们的起点 1 1 1一定在这条路径上, 且我们可以把这条路径分为两个联通块: A A A和 B B B( 1 ∈ B 1\in B 1∈B)。当然也存在 A A A不存在的情况。
我们可以设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示 i i i个点, 最长路径长度为 j j j的竞赛图数量。
首先我们可以知道 d p [ i ] [ 1 ] = 2 ( n − 2 ) ( n − 1 ) 2 dp[i][1]=2^{\frac{(n-2)(n-1)}{2}} dp[i][1]=22(n−2)(n−1), 这是因为实际上我们只需要将所有 1 1 1号点的边都指向 1 1 1号点即可。
然后对于其他 j < i j< i j<i的情况, d p [ i ] [ j ] = d p [ j ] [ j ] × ( i − 1 j − 1 ) × 2 ( i − j ) ( i − j − 1 ) 2 dp[i][j]=dp[j][j]\times \binom{i-1}{j-1}\times 2^{\frac{(i-j)(i-j-1)}{2}} dp[i][j]=dp[j][j]×(j−1i−1)×22(i−j)(i−j−1), d p [ j ] [ j ] dp[j][j] dp[j][j]考虑的 j j j个点边的连接方式, ( i − 1 j − 1 ) \binom{i-1}{j-1} (j−1i−1)考虑选取的点的种类, 相当于钦定 1 1 1号点, 从剩下 i − 1 i-1 i−1个点里面选 j − 1 j-1 j−1个点组成 B B B, 剩下组成 A A A的点集合内部的边乱连即可。
当 i = j i=j i=j的时候, 这个式子不管用了, 但我们可以知道, 包含 i i i个点的竞赛图总数是 2 i ( i − 1 ) 2 2^{\frac{i(i-1)}{2}} 22i(i−1)个, 所以 d p [ i ] [ i ] = 2 i ( i − 1 ) 2 − ∑ j = 1 i − 1 d p [ i ] [ j ] dp[i][i]=2^{\frac{i(i-1)}{2}}-\sum_{j=1}^{i-1}dp[i][j] dp[i][i]=22i(i−1)−∑j=1i−1dp[i][j]。
我们可以 O ( N 2 ) O(N^2) O(N2)预处理组合数, O ( N 2 ) D P O(N^2)DP O(N2)DP即可AC。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 2050
ll pw[MX * MX], dp[MX][MX], C[MX][MX];
ll mod;
int n, bd;
int main(void)
{
scanf("%d%lld", &n, &mod);
bd = n * (n - 1) / 2; R int i, j;
pw[0] = 1; for (i = 1; i <= bd; ++i) pw[i] = pw[i - 1] * 2 % mod;
for (i = 0; i <= n; ++i) C[i][0] = 1;
for (i = 1; i <= n; ++i)
for (j = 1; j <= i; ++j) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
for (i = 1; i <= n; ++i)
{
dp[i][1] = pw[(i - 1) * (i - 2) / 2]; dp[i][i] = pw[i * (i - 1) / 2];
for (j = 2; j < i; ++j) dp[i][j] = dp[j][j] * C[i - 1][j - 1] % mod * pw[(i - j - 1) * (i - j) / 2] % mod;
for (j = 1; j < i; ++j) (dp[i][i] -= dp[i][j]) %= mod;
dp[i][i] = (dp[i][i] + mod) % mod;
}
for (R int i = 1; i <= n; ++i) printf("%lld\n", dp[n][i]);
}