Description
在Byteland一共有n个城市,编号依次为1到n,它们之间计划修建n(n-1)/2条单向道路,对于任意两个不同的点i和j,在它们之间有且仅有一条单向道路,方向要么是i到j,要么是j到i。换句话说,这是一个n个点的竞赛图。Byteasar居住在1号城市,他希望从1号城市出发,沿着单向道路不重复地访问一些城市,使得访问的城市数尽可能多。请写一个程序,帮助Byteasar计算有多少种道路修建方式,使得从1号点出发的最长简单路径经过点数恰好为k,由于答案可能很大,请对P取模输出
Solution
竞赛图缩点后是一条链,最长路就是 1 1 1以及后面的连通分量的大小之和。所以可以直接枚举 1 1 1所在的连通分量大小和后面有多少个点,这样前面就有 n − i − j n-i-j n−i−j个点,设 h i = 2 i ( i − 1 ) 2 h_i=2^{i(i-1)\over2} hi=22i(i−1)为 i i i个点的竞赛图个数,那么前后的方案数分别为 h n − i − j h_{n-i-j} hn−i−j和 h j h_j hj。最难算的是有 i i i个点的竞赛图缩点后变成一块的方案数,这个用到的是图计数中常用的容斥思想,枚举 1 1 1所在连通分量大小,减去不合法方案,即 f i = h i − ∑ j = 1 i − 1 f j × C i j × h i − j f_i=h_i-\sum_{j=1}^{i-1}f_j\times C_i^j\times h_{i-j} fi=hi−j=1∑i−1fj×Cij×hi−j
Code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=2010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,mod,C[Maxn][Maxn],f[Maxn],g[Maxn],h[Maxn],ans[Maxn],fac[Maxn];
int Pow(int x,int y)
{
if(!y)return 1;
int t=Pow(x,y>>1),re=(LL)t*t%mod;
if(y&1)re=(LL)re*x%mod;
return re;
}
void upd(int&x,int y){x+=y;if(x>=mod)x-=mod;}
void pre()
{
C[0][0]=1;
for(int i=1;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])%mod;
}
for(int i=0;i<=n;i++)h[i]=Pow(2,i*(i-1)/2);
g[1]=1,g[2]=0;
for(int i=3;i<=n;i++)
{
g[i]=h[i];
for(int j=1;j<i;j++)
g[i]=(g[i]-(LL)C[i][j]*h[i-j]%mod*g[j]%mod+mod)%mod;
}
fac[0]=1;for(int i=1;i<=n;i++)fac[i]=(LL)fac[i-1]*i%mod;
}
int main()
{
n=read(),mod=read();
pre();
for(int i=1;i<=n;i++)
for(int j=0;i+j<=n;j++)
upd(ans[i+j],(LL)g[i]*C[n-1][i-1]%mod*h[j]%mod*C[n-i][j]%mod*h[n-i-j]%mod);
for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
}