TopCoder SRM 548 1000分 dp+组合数

2 篇文章 0 订阅
1 篇文章 0 订阅

题意

给出 n,m,k ,求出具有 n 个点、m条边、且前 k 个点的点度恰好为2的无向连通图个数,答案对109+7取模。

数据范围

n,m50,k2

解法

首先简化题目,我们考虑k=0的时候,那么有一个很经典的做法是DP。我们用 dp[i][j] 表示i个点m条边的连通图个数,那么答案明显是 dp[n][m]
接下来的转移我们考虑补集转化,我们枚举1号点所在的联通块点数和边数,那么
dp[i][j]=CjC2ii1k=1min(j,C2k)l=k1dp[k][l]Ck1i1CjlC2ik
那么我们已经完美解决了k=0的情况,接下来我们分析一下k=1的情况,由于1号点的度数为2,那么我们可以得出,删掉1号点及其连边,剩余的图要么只有1个联通块要么只有2个联通块。
对于只有1个联通块的情况,明显就是
dp[n1,m2]C2n1
对于有2个联通块的情况,我们依然可以枚举2号点所在的联通块大小及边数,答案就是
n2i=1min(C2i,m2)j=i1dp[i][j]dp[n1i][m2j]i(n1i)Ci1n2
于是k=1的情况也被解决了 作为一条咸鱼,我选择放弃k=2
其实k=2和k=1的情况差不多,只不过要讨论的情况变多了而已。说的简单啊喂
那么我们将1号点和2号点提出来,发现其余点和边构成的图的联通块个数只有1,2,3个。
当1号点与2号点相连的时候,联通块只可能为1或者2。
若联通块为1,那么答案就是
dp[n2][m3](n2)(n2)
若联通块为2,其实也就等于了k=1的情况,不过我们要把答案×2,因为跟1号点连边和2号点连边是两种不同的情况。
2n3i=1min(C2i,m3)j=i1dp[i][j]dp[n2i][m3j]i(n2i)Ci1n3
当1号点与2号点不相连的时候,若联通块个数为1,那么答案就是
dp[n2][m4]C2n2C2n2
若联通块个数为2,我们就枚举3号点所在的联通块大小及边数,那么答案是 啊公式真的好难打
n3i=1min(C2i,k4)j=i1dp[i][j]dp[n2i][m4j]Ci1n3i(n2i)(2(C2i+C2n2i)+i(n2i) )
最后的那个括号里面的表示1个点连接两个联通块,另一个点要么两条边都在同一个联通块内,要么也连接两个联通块,注意后面的不×2。
若联通块个数为3,那么很明显1 2号线都要连接两个联通块,这时候我们枚举第1个联通块(设为K1),第二个联通块(K2)的点数和边数,那么第三个联通块(k3)的点数和边数也就确定了。说实话我觉得这个公式我要打半小时
n4v1=1min(C2v1,m4)e1=v11dp[v1][e1]Cv1n2
v1+v2<=n3v2=1min(C2v2,me14)e2=v21dp[v2][e2]Cv2n2v1
dp[n2v1v2][m4e1e2]
v1v2(n2v1v2)(n2)2
以上4行就是计算答案的过程了(自动在换行处补*)
最后那个式子v1*v2*(n-2-v1-v2)*(n-2)*2是这样算的,我们枚举1号点连接的两个联通块的方案数,再枚举2号点的,那么就有式子
v1v2(v1v3+v2v3)+v1v3(v1v2+v2v3)+v2v3(v1v2+v1v3)
合并一下就是
v1v2v3v1+v2+v3)2=v1v2v3(n2)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;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值