最长路径
在Byteland一共有(n)个城市,编号依次为(1)到(n),它们之间计划修建(inom{n}{2})条单向道路,对于任意两个不同的点(i)和(j),在它们之间有且仅有一条单向道路,方向要么是(i)到(j),要么是(j)到(i)。换句话说,这是一个(n)个点的竞赛图。
Byteasar居住在(1)号城市,他希望从(1)号城市出发,沿着单向道路不重复地访问一些城市,使得访问的城市数尽可能多。
请写一个程序,帮助Byteasar计算有多少种道路修建方式,使得从(1)号点出发的最长简单路径经过点数恰好为(k),由于答案可能很大,请对(P)取模输出。
题解
竞赛图一定存在哈密顿路径。
对于(n)个点的竞赛图,(1)号点能作为哈密顿路径起点的充要条件是(1)能到剩余(n-1)个点。
考虑归纳证明,(n=1,2)时显然成立。对于(ngeq 3)的情况,显然(1)能到剩余(n-1)个点是必要的,不然何谈哈密顿路径。证明充分性考虑构造,我们(1)至少能到剩余(n-1)个点导出子图的所有哈密顿路径的起点中至少一个,不然与假设矛盾。证毕。
所以这道题要让最长路为(k)只需要让(1)能到(k)个点,其余(n-k)个点不能到即可。
设(f_n)为(1)能作为哈密顿路径起点的(n)个点的竞赛图数量,(g_n=2^{inom{n}{2}})。
[f_n=g_n-sum_{i=1}^{n-1}inom{n-1}{i-1}f_ig_{n-i}
]
最后有( ext{ans}_k=inom{n-1}{k-1}f_kg_{n-k})。时间复杂度(O(n^2))。
CO int N=2e3+10;
int C[N][N],G[N],F[N];
int main(){
int n=read<int>(),P=read<int>();
for(int i=0;i<=n;++i){
C[i][0]=1;
for(int j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
}
for(int i=0;i<=n;++i) G[i]=fpow(2,i*(i-1)/2,P);
for(int i=1;i<=n;++i){
F[i]=G[i];
for(int j=1;j<=i-1;++j) F[i]=(F[i]+P-(int64)C[i-1][j-1]*F[j]%P*G[i-j]%P)%P;
}
for(int i=1;i<=n;++i){
int ans=(int64)C[n-1][i-1]*F[i]%P*G[n-i]%P;
printf("%d
",ans);
}
return 0;
}
然后是老生常谈的生成函数优化。其实这个式子跟有标号连通无向图的一模一样。
[F'=G'-F‘(G-1)
]
[F'=frac{G'}{G}
]
[F=ln Gleftrightarrow G=e^F
]
时间复杂度(O(nlog n))。