codeforces814E. An unavoidable detour for home

题意:

d i s x dis_x disx为x到1的唯一最短路(必须唯一),则满足每个点度数= d x d_x dx d i s x &lt; = d i s x + 1 dis_x&lt;=dis_{x+1} disx<=disx+1的图有多少种
n &lt; = 50 , 2 &lt; = d x &lt; = 3 n&lt;=50,2&lt;=d_x&lt;=3 n<=50,2<=dx<=3

题解:

显然按 d i s dis dis这个图将这个图划分成一段段,每个点会向前一段连一条边,剩下的边就是段内互连
一个比较naive的想法就是暴力枚举段的划分方法,然后状压上一段每个点的剩余度数dp,当需要新开一段的时候,也就是需要段内互连的时候,转移需要预处理一个 g [ i ] [ j ] g[i][j] g[i][j]表示i个度数为1的点,j个读度数为2的点(有标号但无顺序)互连的方案数,这个很好dp具体代码
容易发现具体哪个点剩余度数是无所谓的,我们只关心度数为1的点和度数为2的点有多少
于是可以直接dp, f [ i ] [ j ] [ a ] [ b ] f[i][j][a][b] f[i][j][a][b]表示到i个前一段的右端点是j,前一段的1,2度数个数分别为a,b直接转移即可
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const LL mod=1e9+7;
LL g[55][55],d[55],n;
LL c[3][55],f[55][55][55][55];
LL C(LL n) {return n*(n-1)/2%mod;}
void add(LL &x,LL y) {x+=y;x-=x>=mod?mod:0;}
void pre()
{
	g[0][0]=1;
	for(LL i=2;i<=n;i+=2) g[i][0]=(i-1)*g[i-2][0]%mod;
	for(LL j=1;j<=n;j++)
		for(LL i=0;i+j<n;i++)
		{
			if(j>=3) (g[i][j]+=C(j-1)*g[i+2][j-3]%mod)%=mod;
			if(j>=2) (g[i][j]+=(j-1)*i%mod*g[i][j-2]%mod)%=mod;
			if(i>=2) (g[i][j]+=C(i)*g[i-2][j-1]%mod)%=mod;
		}
}
LL get(LL l,LL r,LL op) {return c[op][r]-c[op][l];}
int main()
{
	scanf("%lld",&n);
	for(LL i=1;i<=n;i++) scanf("%lld",&d[i]);
	pre();
	for(LL i=2;i<=n;i++)
	{
		c[1][i]=c[1][i-1];c[2][i]=c[2][i-1];
		c[d[i]-1][i]++;
	}
	f[d[1]+1][d[1]+1][c[1][d[1]+1]][c[2][d[1]+1]]=1;
	for(LL i=d[1]+1;i<=n;i++)
	for(LL j=1;j<=i;j++)
	for(LL a=0;a<=i;a++)
	for(LL b=0;a+b<=i;b++) if(f[i][j][a][b])
	{
		if(i!=j) add(f[i][i][get(j,i,1)][get(j,i,2)],f[i][j][a][b]*g[a][b]%mod);
		if(i!=n&&a) add(f[i+1][j][a-1][b],f[i][j][a][b]*a%mod);
		if(i!=n&&b) add(f[i+1][j][a+1][b-1],f[i][j][a][b]*b%mod);
	}
	LL ans=0;
	for(LL a=0;a<=n;a++)
		for(LL b=0;a+b<=n;b++)
		{
			if(!f[n][n][a][b]) continue;
			add(ans,f[n][n][a][b]*g[a][b]%mod);
		}
	printf("%lld",ans);
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值