计算机科学中,图可以看做是点集和边集所组成的二元组。
一个图被认为是刚体,如果该图无法只改变其中一部分的形状,而使得余下的部分的形状保持不变。
通过给每个点设置一个平面坐标,图可以镶嵌在欧几里得平面中。

一个图被认为是刚体,如果该图无法只改变其中一部分的形状,而使得余下的部分的形状保持不变。
例如上图中的 (a) (b) (c) 都是刚体。
为了简化问题,我们现在只考虑
n × m 的格点图。

上图不是刚体,但是可以通过在对角线加入支撑的方式使得其变为刚体。

我们的问题是,对于 m × n 的个点图,有多少种添加支撑的方案可以使其变为刚体?
注意本题在每个小矩形中,我们至多只允许添加一个方向的对角线。
例如,对于 2 × 3 的格点图,一共有 448 种方案。
例如,对于 2 × 3 的格点图,一共有 448 种方案。
思路:
如果要是一个刚体稳定,那么要使每一行和每一列都至少有一个对角线,转化为一个二分图左边有n个节点,右边有m个节点,添加边使得图连通。
二分图连通图计数:
dp[n][m]=3^(n*m)-dp[i][j]*C[n-1][i-1]*C[m][j]*3^( (n-i)*(m-j) ) (0<=i<=n,0<=j<=m)
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
long long Pow[101],dp[11][11];
int C[11][11];
//dp[i][j]表示二分图中左边有i个点,右边有j个点,且i个点中一定包括1这个点的方案数
int main(){
int n,m;
for(int i=0;i<=10;i++)
C[i][0]=1;
for(int i=1;i<=10;i++)
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
Pow[0]=1;
for(int i=1;i<=100;i++)
Pow[i]=Pow[i-1]*3%MOD;
while(scanf("%d%d",&n,&m)!=EOF){
memset(dp,0,sizeof(dp));
dp[1][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
dp[i][j]=Pow[i*j];
for(int a=1;a<=i;a++)
for(int b=0;b<=j;b++){
if(i==a&&j==b)
continue;
dp[i][j]=((dp[i][j]-dp[a][b]*C[i-1][a-1]*C[j][b]%MOD*Pow[(i-a)*(j-b)]%MOD)%MOD+MOD)%MOD;
}
}
printf("%lld\n",dp[n][m]);
}
return 0;
}