1. 程式人生 > >BZOJ5219[Lydsy2017省隊十連測] 最長路徑

BZOJ5219[Lydsy2017省隊十連測] 最長路徑

最長路徑

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

題解

dp[i][j]dp[i][j]表示ii個點,從11出發的最長路徑為jj的競賽圖方案數,就有轉移如下: dp[i][1]=2(i1)×(i2)2dp[i][j]=j=2i1dp[j][j]×(i1j1)×2(ij)×(ij1)2dp[i][i]=2i×(i1)2j=1i1dp[i][j] dp[i][1]=2^{\frac{(i-1)\times(i-2)}{2}}\\ dp[i][j]=\sum_{j=2}^{i-1}dp[j][j]\times \binom{i-1}{j-1}\times 2^{\frac{(i-j)\times(i-j-1)}{2}}\\ dp[i][i]=2^{\frac{i\times(i-1)}{2}}-\sum_{j=1}^{i-1}dp[i][j]

稍微解釋一下第二個轉移方程,考慮從jj個點,從11出發的最長路為jj的圖上擴充套件:先從ii個點裡選出jj個構成dp[j][j]dp[j][j]的店,因為一定有11號點,所以方案數為(i1j1)\binom{i-1}{j-1},然後剩下的iji-j個點向第11個點連邊,iji-j個點亂連一波,構造完成。

程式碼
#include<bits/stdc++.h>
using namespace std;
const int M=2005;
int n,p;
long long C[M][M],dp[M][M],pao[M*M];
void in(){scanf("%d%d",&n,&p);}
void ac()
{
	pao[0]=1;for(int i=1;i<=n*n;++i)pao[i]=(pao[i-1]<<1)%p;
	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]+C[i-1][j-1])%p;
	for(int i=1;i<=n;++i)
	{
		dp[i][1]=pao[(i-1)*(i-2)/2],dp[i][i]=pao[i*(i-1)/2];
		for(int j=2;j<i;++j)dp[i][j]=dp[j][j]*C[i-1][j-1]%p*pao[(i-j)*(i-j-1)/2]%p;
		for(int j=1;j<i;++j)(dp[i][i]-=dp[i][j])%=p;
	}
	for(int i=1;i<=n;++i)printf("%lld\n",(dp[n][i]+p)%p);
}
int main(){in();ac();}