题意
给出
n,m,k
,求出具有
n
个点、
数据范围
n,m≤50,k≤2
解法
首先简化题目,我们考虑k=0的时候,那么有一个很经典的做法是DP。我们用
dp[i][j]
表示i个点m条边的连通图个数,那么答案明显是
dp[n][m]
。
接下来的转移我们考虑补集转化,我们枚举1号点所在的联通块点数和边数,那么
dp[i][j]=CjC2i−∑i−1k=1∑min(j,C2k)l=k−1dp[k][l]∗Ck−1i−1∗Cj−lC2i−k
那么我们已经完美解决了k=0的情况,接下来我们分析一下k=1的情况,由于1号点的度数为2,那么我们可以得出,删掉1号点及其连边,剩余的图要么只有1个联通块要么只有2个联通块。
对于只有1个联通块的情况,明显就是
dp[n−1,m−2]∗C2n−1
对于有2个联通块的情况,我们依然可以枚举2号点所在的联通块大小及边数,答案就是
∑n−2i=1∑min(C2i,m−2)j=i−1dp[i][j]∗dp[n−1−i][m−2−j]∗i∗(n−1−i)∗Ci−1n−2
于是k=1的情况也被解决了 作为一条咸鱼,我选择放弃k=2
其实k=2和k=1的情况差不多,只不过要讨论的情况变多了而已。说的简单啊喂
那么我们将1号点和2号点提出来,发现其余点和边构成的图的联通块个数只有1,2,3个。
当1号点与2号点相连的时候,联通块只可能为1或者2。
若联通块为1,那么答案就是
dp[n−2][m−3]∗(n−2)∗(n−2)
若联通块为2,其实也就等于了k=1的情况,不过我们要把答案×2,因为跟1号点连边和2号点连边是两种不同的情况。
2∗∑n−3i=1∑min(C2i,m−3)j=i−1dp[i][j]∗dp[n−2−i][m−3−j]∗i∗(n−2−i)∗Ci−1n−3
当1号点与2号点不相连的时候,若联通块个数为1,那么答案就是
dp[n−2][m−4]∗C2n−2∗C2n−2
若联通块个数为2,我们就枚举3号点所在的联通块大小及边数,那么答案是 啊公式真的好难打
∑n−3i=1∑min(C2i,k−4)j=i−1dp[i][j]∗dp[n−2−i][m−4−j]∗Ci−1n−3∗i∗(n−2−i)∗(2∗(C2i+C2n−2−i)+i∗(n−2−i) )
最后的那个括号里面的表示1个点连接两个联通块,另一个点要么两条边都在同一个联通块内,要么也连接两个联通块,注意后面的不×2。
若联通块个数为3,那么很明显1 2号线都要连接两个联通块,这时候我们枚举第1个联通块(设为K1),第二个联通块(K2)的点数和边数,那么第三个联通块(k3)的点数和边数也就确定了。说实话我觉得这个公式我要打半小时
∑n−4v1=1∑min(C2v1,m−4)e1=v1−1dp[v1][e1]∗Cv1n−2
∑v1+v2<=n−3v2=1∑min(C2v2,m−e1−4)e2=v2−1dp[v2][e2]∗Cv2n−2−v1
dp[n−2−v1−v2][m−4−e1−e2]
v1∗v2∗(n−2−v1−v2)∗(n−2)∗2
以上4行就是计算答案的过程了(自动在换行处补*)
最后那个式子v1*v2*(n-2-v1-v2)*(n-2)*2是这样算的,我们枚举1号点连接的两个联通块的方案数,再枚举2号点的,那么就有式子
v1∗v2∗(v1∗v3+v2∗v3)+v1∗v3∗(v1∗v2+v2∗v3)+v2∗v3∗(v1∗v2+v1∗v3)
合并一下就是
v1∗v2∗v3∗(v1+v2+v3)∗2=v1∗v2∗v3∗(n−2)∗2
到此为止这道题也就完美解决了
收获
这个题的入手点就是我们发现k很小,只有三种情况。
那么我们从简到难,先发现k=0的情况可以由DP解决。
再由限制的度数为2来推出除去限制点以外的联通块个数不会太多,于是我们分类讨论情况就可以了。
考点:
组合数 DP
易错点:
漏情况 边界(这个其实可以用一些技巧忽视掉) 少取膜
一个月以内我不会再写一篇公式这么多的题解了QAQ
#define mN 2510
#define mod 1000000007
#define ms(a,b) memset(a,b,sizeof(a))
#define ll long long
#define min(a,b) (a<b?a:b)
class KingdomAndCities {
public:
ll c[mN][61];
ll dp[61][61];
int howMany(int n, int k, int m) {
ms(c,0);
c[0][0]=1;
if (m<n-1) return 0;
for (int i=1; i<=n*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;
}
ms(dp,0);
for (int i=1; i<=n; i++)
for (int j=i-1; j<=min(c[i][2],m); j++) {
dp[i][j]=c[c[i][2]][j];
for (int k=1; k<=i-1; k++)
for (int l=k-1; l<=min(c[k][2],j); l++)
dp[i][j]=(dp[i][j]-dp[k][l]*c[i-1][k-1]%mod*c[c[i-k][2]][j-l]%mod+mod)%mod;
}
if (k==0) return dp[n][m]%mod;
if (k==1) {
int ans=0;
ans+=dp[n-1][m-2]*c[n-1][2]%mod;
for (int i=1; i<=n-2; i++)
for (int j=i-1; j<=min(c[i][2],m-2); j++) {
ans=(ans+dp[i][j]*dp[n-1-i][m-2-j]%mod*i%mod*(n-1-i)%mod*c[n-2][i-1]%mod)%mod;
}
return ans%mod;
}
if (k==2) {
int ans=0;
ans=dp[n-2][m-3]*(n-2)%mod*(n-2)%mod;
for (int i=1; i<=n-3; i++)
for (int j=i-1; j<=min(c[i][2],m-3); j++) {
ans=(ans+2*dp[i][j]%mod*dp[n-2-i][m-3-j]%mod*i%mod*(n-2-i)%mod*c[n-3][i-1]%mod)%mod;
}
ans=(ans+dp[n-2][m-4]*c[n-2][2]%mod*c[n-2][2]%mod)%mod;
for (int i=1; i<=n-3; i++)
for (int j=i-1; j<=min(c[i][2],m-4); j++) {
ans=(ans+dp[i][j]*dp[n-2-i][m-4-j]%mod*i%mod*(n-2-i)%mod*c[n-3][i-1]%mod*((2*(c[i][2]+c[n-2-i][2])%mod+i*(n-2-i)%mod)%mod)%mod)%mod;
}
for (int v1=1; v1<=n-4; v1++)
for (int e1=v1-1; e1<=min(c[v1][2],m-4); e1++)
for (int v2=1; v1+v2<=n-3; v2++)
for (int e2=v2-1; e2<=min(c[v2][2],m-e1-4); e2++)
ans=(ans+dp[v1][e1]*c[n-3][v1-1]%mod*dp[v2][e2]%mod*c[n-3-v1][v2-1]%mod*dp[n-2-v1-v2][m-4-e1-e2]%mod*v1%mod*v2%mod*(n-2-v1-v2)%mod*(n-2)%mod*2%mod)%mod;
return ans;
}
return 0;
}
};