题目链接
题目解法
超妙的 dp 题
考虑把题目转化为二分图匹配
假设每对点下面都有一条黑线,那么怪异度即为跨过黑线的总数
考虑令
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k] 为到
i
i
i 对,前面有
j
j
j 对未匹配,当前怪异度为
k
k
k 的方案数
考虑转移:
若
i
,
j
i,j
i,j 匹配,则不会新增未匹配对数,
d
p
[
i
]
[
j
]
[
k
]
+
=
d
p
[
i
−
1
]
[
j
]
[
k
−
2
j
]
dp[i][j][k]+=dp[i-1][j][k-2j]
dp[i][j][k]+=dp[i−1][j][k−2j],其中
k
−
2
j
k-2j
k−2j 的含义是前面
2
j
2j
2j 个未匹配会跨越当前的黑线,也可以理解为价值提前计算
若
i
i
i 和右部点匹配 或
j
j
j 和左部点匹配,则不会新增未匹配对数,
d
p
[
i
]
[
j
]
[
k
]
+
=
2
j
∗
d
p
[
i
−
1
]
[
j
]
[
k
−
2
j
]
dp[i][j][k]+=2j*dp[i-1][j][k-2j]
dp[i][j][k]+=2j∗dp[i−1][j][k−2j]
若
i
,
j
i,j
i,j 都不匹配,则会新增一对未匹配点,
d
p
[
i
]
[
j
]
[
k
]
+
=
d
p
[
i
−
1
]
[
j
−
1
]
[
k
−
2
j
]
dp[i][j][k]+=dp[i-1][j-1][k-2j]
dp[i][j][k]+=dp[i−1][j−1][k−2j]
若
i
,
j
i,j
i,j 都与之前未匹配的匹配,则会减少一对未匹配点,
d
p
[
i
]
[
j
]
[
k
]
+
=
(
j
+
1
)
∗
(
j
+
1
)
∗
d
p
[
i
−
1
]
[
j
+
1
]
[
k
−
2
j
]
dp[i][j][k]+=(j+1)*(j+1)*dp[i-1][j+1][k-2j]
dp[i][j][k]+=(j+1)∗(j+1)∗dp[i−1][j+1][k−2j]
时间复杂度 O ( n 2 k ) O(n^2k) O(n2k)
#include <bits/stdc++.h>
using namespace std;
const int N(60),P(1e9+7);
int n,m,dp[N][N][N*N];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int main(){
n=read(),m=read();
dp[0][0][0]=1;
for(int i=1;i<=n;i++)
for(int j=0;j<=i;j++)
for(int k=2*j;k<=m;k++){
int add=(1ll*(2*j+1)*dp[i-1][j][k-2*j]+1ll*(j+1)*(j+1)*dp[i-1][j+1][k-2*j])%P;
if(j>0) add=(add+dp[i-1][j-1][k-2*j])%P;
(dp[i][j][k]+=add)%=P;
}
// cout<<dp[1][1][0]<<'\n';
printf("%d",dp[n][0][m]);
return 0;
}
/*
dp[i][j][k]:二分图上到第i个点,有j个点未匹配,当前怪异度为k的方案数
*/