[BZOJ]5219: [Lydsy2017省队十连测]最长路径 DP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_36797646/article/details/88387172

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 所在的连通分量大小和后面有多少个点,这样前面就有 n i j n-i-j 个点,设 h i = 2 i ( i 1 ) 2 h_i=2^{i(i-1)\over2} i i 个点的竞赛图个数,那么前后的方案数分别为 h n i j h_{n-i-j} h j h_j 。最难算的是有 i i 个点的竞赛图缩点后变成一块的方案数,这个用到的是图计数中常用的容斥思想,枚举 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}

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]);
}

猜你喜欢

转载自blog.csdn.net/baidu_36797646/article/details/88387172