BZOJ 5219: [Lydsy2017省队十连测]最长路径 竞赛图组合计数

title

BZOJ 5219
Description

在Byteland一共有n个城市,编号依次为1到n,它们之间计划修建n(n-1)/2条单向道路,对于任意两个不同的点i和j,在它们之间有且仅有一条单向道路,方向要么是i到j,要么是j到i。换句话说,这是一个n个点的竞赛图。Byteasar居住在1号城市,他希望从1号城市出发,沿着单向道路不重复地访问一些城市,使得访问的城市数尽可能多。
请写一个程序,帮助Byteasar计算有多少种道路修建方式,使得从1号点出发的最长简单路径经过点数恰好为k,由于答案可能很大,请对P取模输出

Input

第一行包含两个正整数n,P,表示点数和模数。2≤P≤1e9,N<=2000

Output

输出n行,第i行输出从1出发的最长简单路径经过点数恰好为i的竞赛图个数模P。

Sample Input

2 233

Sample Output

1
1

Source

Claris原创,本站版权所有

analysis

竞赛图有个很好的性质:一定存在一条哈密尔顿路径。

所以我们的起点 \(1\) 一定在这条路径上, 且我们可以把这条路径分为两个联通块: \(A\)\(B\)\(1\in B\))。当然也存在 \(A\) 不存在的情况。

我们可以设 \(f[i][j]\)表示 \(i\) 个点, 最长路径长度为 \(j\)的竞赛图数量。

首先我们可以知道 \(f[i][1]=2^{\frac{(n-2)(n-1)}{2}}\), 这是因为实际上我们只需要将所有 \(1\) 号点的边都指向 \(1\) 号点即可。

然后对于其他 \(j<i\) 的情况,\(f[i][j]=f[j][j]\times \binom{i-1}{j-1}\times 2^{\frac{(i-j)(i-j-1)}{2}}\)\(f[j][j]\) 考虑的 \(j\) 个点边的连接方式, \(\binom{i-1}{j-1}\)考虑选取的点的种类, 相当于钦定 \(1\) 号点, 从剩下 \(i−1\) 个点里面选 \(j−1\) 个点组成 \(B\), 剩下组成 \(A\) 的点集合内部的边乱连即可。

\(i=j\) 的时候, 这个式子不管用了, 但我们可以知道, 包含 \(i\) 个点的竞赛图总数是\(2^{\frac{i(i-1)}{2}}\)个, 所以 \(f[i][i]=2^{\frac{i(i-1)}{2}}-\sum_{j=1}^{i-1}f[i][j]\)

所以可以\(O(N^2)\)预处理组合数, \(O(N^2)DP\)

参考资料:LPA20020220

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2050;
 
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
 
template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) putchar(ch[num--]);
}
 
ll power[maxn*maxn],C[maxn][maxn],f[maxn][maxn],mod;
int main()
{
    int n;read(n);read(mod);
    int e=n*(n-1)>>1;power[0]=1;
    for (int i=1; i<=e; ++i) power[i]=(power[i-1]<<1)%mod;
    for (int i=0; i<=n; ++i) C[i][0]=1;
    for (int i=1; i<=n; ++i)
        for (int j=1; j<=i; ++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    for (int i=1; i<=n; ++i)
    {
        f[i][1]=power[(i-1)*(i-2)>>1];
        f[i][i]=power[i*(i-1)>>1];
        for (int j=2; j<i; ++j) f[i][j]=f[j][j]*C[i-1][j-1]%mod*power[(i-j-1)*(i-j)>>1]%mod;
        for (int j=1; j<i; ++j) (f[i][i]-=f[i][j])%=mod;
        (f[i][i]+=mod)%=mod;
    }
    for (int i=1; i<=n; ++i) write(f[n][i]),puts("");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/G-hsm/p/11323292.html