分层图DP--Distance

题意
给定 n n n个点,要求从 1 1 1 2 2 2的最短路为 d d d,点之间可以连一条双向边,也可以不连,问合法的图有多少种,答案 m o d   p mod\ p mod p
n ≤ 150 n\le150 n150

solution:
分层图 d p dp dp
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示距离 1 1 1点为 i i i,总共选了 j j j个点, i i i这一层选了 k k k个点
考虑转移,要把 2 2 2这个点拿出来,到最后第 d − 1 d-1 d1 d d d层的时候再考虑,因此前面要考虑前 d − 1 d-1 d1层的
首先枚举第 i + 1 i+1 i+1层选 l l l个点,那么选出来这 l l l个点有 C n − j − 1 l C_{n-j-1}^l Cnj1l种方案,然后这 l l l个点可以连那 k k k个点也可以不连,但不能全都不连,所以还要乘上 ( 2 k − 1 ) l (2^k-1)^l (2k1)l,还有这 l l l个点内部可以任意连,所以还要乘 2 l ∗ ( l − 1 ) 2 2^{\frac{l*(l-1)}{2}} 22l(l1)
算完前 d − 1 d-1 d1层,单独考虑第 d d d层,首先枚举 i , j i,j i,j表示第 d − 1 d-1 d1层前共选了 i i i个点,第 d − 1 d-1 d1层选了 j j j个, 2 2 2这个点可以和 j j j中任意一个点连但不能都不连,同时剩下 n − i − 1 n-i-1 ni1个可以和 j j j个点任意连, n − i n-i ni个点也可以任意互相连。

注意这个复杂度看起来是 n 4 n^4 n4还得带个 l o g log log,但我们可以用预处理去掉 l o g log log,然后通过卡边界来降低复杂度让它可过

附上代码:

#include
#include
#include
#include
#include
#define LL long long
#define maxn 155
using namespace std;
int n,d,mod;
LL bin[maxn*100],fac[maxn],inv[maxn],pw[maxn*100][maxn],ans,f[maxn][maxn][maxn];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

inline void upd(LL &x,LL y){
	x+=y; x>=mod?x-=mod:0;
}

inline LL qpow(LL x,int k){
	LL ret=1;
	while(k){
		if(k&1) (ret*=x)%=mod;
		(x*=x)%=mod; k>>=1;
	} return ret;
}

inline void prework(){
	fac[0]=1; bin[0]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	for(int i=1;i<=n*n/2;i++) bin[i]=(bin[i-1]<<1)%mod;
	inv[n]=qpow(fac[n],mod-2);
	for(int i=n;i;i--) inv[i-1]=inv[i]*i%mod;
	for(int i=0;i<=n*n/2;i++){//2^i
		LL x=bin[i]-1; pw[i][0]=1;
		for(int j=1;j<=n;j++)
			pw[i][j]=pw[i][j-1]*x%mod;
	}
}

inline LL C(int n,int m){
	if(n<m) return 0;
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int main(){
	freopen("distance.in","r",stdin);
	freopen("distance.out","w",stdout);
	n=rd(); d=rd(); mod=rd(); prework();
	f[0][1][1]=1;
	for(int i=0;i<d-1;i++)//第i层 
		for(int j=i+1;j<=n-d+i;j++)//选了j个点 
			for(int k=1;k<=j-i;k++)//第i层选k个 
				if(f[i][j][k])
				for(int l=1;l<=n-j-d+i+1;l++)//i+1层选l个 
	upd(f[i+1][j+l][l],(LL)f[i][j][k]*C(n-j-1,l)%mod*pw[k][l]%mod*bin[l*(l-1)>>1]%mod);
	for(int i=d;i<n;i++)//第d-1层及以前选了i个点 
		for(int j=1;j<=i-d+1;j++)//第d-1层选了j个 
	upd(ans,f[d-1][i][j]*(bin[j]-1+mod)%mod*bin[(n-i-1)*j]%mod*bin[(n-i)*(n-i-1)>>1]%mod);
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(分层图DP)