HDU 5729 Rigid Frameworks (联通块计数问题)

题目传送门

通过看题解画图可以发现:

不论怎么转,一列里的横边/一行里的竖边始终平行

当我们加固一个格子时,会让它所在的这一行的竖边和这一列的横边保证垂直

而我们的目标是求所有竖边和横边都保证垂直的方案数

把一行里的所有竖边看成一个点,把一列里的所有横边看成一个点。一共$n+m$个点

把图看成二分图,左侧$n$个点,右侧$m$个点。加固一个格子相当于在左侧的一个点和右侧的一个点之间连边!

我们的问题变成了求解二分图的连通图个数!

接下来就是很套路的$DP$了

定义$f(a,b)$表示左边$a$个点,右边$b$个点的连通二分图个数

对于连通图问题,我们依然采用常规的“固定思想”,我们固定左侧第一个点

直接求联通很困难,考虑用不合法的情况相减,可得$DP$方程:

$f(a,b)=3^{ab}-\sum_{i=0}^{a}\sum_{j=0}^{b}f(i,j)C_{a-1}^{i-1}C_{b}^{j}3^{(a-i)(b-j)}$

(注意i=a,j=b是不能转移的)

初值怎么赋需要思考

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N1 65
 5 #define M1 3605
 6 #define ll long long
 7 using namespace std;
 8 const ll p=1000000007;
 9 
10 int n,m,T;
11 int pw3[M1],C[N1][N1],f[N1][N1];
12 
13 int main()
14 {
15     int i,j,a,b; n=60; m=60;
16     for(i=1,pw3[0]=1;i<=n*m;i++) pw3[i]=3ll*pw3[i-1]%p;
17     C[0][0]=1;
18     for(i=1;i<=max(n,m);i++)
19     {
20         C[i][0]=C[i][i]=1;
21         for(j=1;j<i;j++)
22             C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
23     }
24     f[1][0]=1; f[1][1]=2; //pw3[0]=0;
25     for(a=1,b=2;b<=m;b++)
26     {
27         f[a][b]=pw3[a*b];
28         for(j=0,i=1;j<b;j++)
29         {
30             f[a][b]=(f[a][b]-1ll*f[i][j]*C[a-1][i-1]%p*C[b][j]%p*pw3[(a-i)*(b-j)]%p+p)%p;
31         }
32     }
33     for(a=2;a<=n;a++)
34     {
35         for(b=1;b<=m;b++)
36         {
37             f[a][b]=pw3[a*b];
38             for(i=1;i<a;i++)
39             for(j=0;j<=b;j++)
40                 f[a][b]=(f[a][b]-1ll*f[i][j]*C[a-1][i-1]%p*C[b][j]%p*pw3[(a-i)*(b-j)]%p+p)%p;
41             for(j=0,i=a;j<b;j++)
42                 f[a][b]=(f[a][b]-1ll*f[i][j]*C[a-1][i-1]%p*C[b][j]%p*pw3[(a-i)*(b-j)]%p+p)%p;
43         }
44     }
45     while(scanf("%d%d",&n,&m)!=EOF)
46     {    
47         printf("%d\n",f[n][m]);
48     }
49     return 0;
50 }

 

posted @ 2019-03-20 18:45  guapisolo  阅读(258)  评论(0编辑  收藏  举报