【JZOJ5061】【GDOI2017第二轮模拟day1】最长路径

Description

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

Data Constraint

【JZOJ5061】【GDOI2017第二轮模拟day1】最长路径_第1张图片

Solution

对于不太喜欢用脑子的我来说,10分钟打出前60分后走人……
但现在是时候正面杠这道题了……
根据某位大牛的理论,竞赛图有以下性质:
1、竞赛途中必存在一条哈比顿路径。
2、若把在同一个强连通分量的点合并,并然会形成一条链的形式,即不会产生分叉。
别问我怎么证明,理解不到,就背到。
我们设出f[i]表示i个点时竞赛图的方案,g[i]表示i个点时全图构成一个强连通分量的方案。

f[n]=2n(n1)/2

g[n]=f[n]i=1n1g[i]f[ni]Cin

那么我们现在设i表示1所在的强连通分量的大小,j表示1之后有多少个点。
ans[i+j]=g[i]Ci1n1f[j]f[nij]Cjnij

Code

#include
#include
#include
#include
#include
#define ll long long
using namespace std;
const ll maxn=2e3+5;
ll f[maxn],g[maxn],c[maxn][maxn],ans[maxn];
ll n,mo,i,t,j,k,l,x,y,z;
ll mi(ll y){
    if (y==1) return 2;
    ll t=mi(y/2);
    if (y%2) return t*t%mo*2%mo;return t*t%mo;
}
int main(){
    freopen("path.in","r",stdin);freopen("path.out","w",stdout);
    scanf("%lld%lld",&n,&mo);
    c[0][0]=1;
    for (i=1;i<=n;i++){
        c[i][0]=1;
        for (j=1;j<=i;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
    }
    f[0]=g[0]=f[1]=g[1]=1;
    for (i=1;i<=n;i++){
        if (i>1)f[i]=g[i]=mi(i*(i-1)/2);
        for (j=1;j*f[i-j]%mo*c[i][j]%mo;
        g[i]=(g[i]%mo+mo)%mo;
    }
    for (i=1;i<=n;i++){
        for (j=0;j<=n-i;j++)
            ans[i+j]+=c[n-1][i-1]*c[n-i][j]%mo*g[i]%mo*f[j]%mo*f[n-i-j]%mo;
    }
    for (i=1;i<=n;i++)
        ans[i]%=mo,printf("%lld\n",ans[i]);
}

你可能感兴趣的:(GDOI,dp)