相当棒的一道题,主要是注意一些性质:
1.所有小正方形的边长都相等那么很明显这是一个平行四边形。菱行
2.平行具有传递性,也就是说如果某两条边垂直,那么那些平行的边也相互垂直!!!这是此题关键
3.我们的目标是所有小正方形的边一直保持垂直。
4.如果你此前做过关于二分图的一些题的话(网格题一般都会有二分图~~~~)你就会发现此题就是求联通图的数目,直接dp就好了
>_<mod写错,导致debug好久~~~~
写联通图dp的关键就是你拿一个点出来,然后进行讨论。。网上很多教程的。。。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
double kk = 1e-12;
ll c[11][11], cut[11][11], link[11][11],fact[110];
void init()
{
fact[0] = 1;
for (int i = 1; i <= 105; i++)
fact[i] = fact[i - 1] * 3,
fact[i] %= mod;
for (int i = 0; i <= 10; i++)
{
cut[0][i] = 1; cut[i][0] = 1;
link[0][i] = 0; link[i][0] = 0;
}
cut[1][1] = 1; link[1][1] = 2;
c[0][0] = 1;
for (int i = 1; i <= 10; i++)
{
c[i][0] = 1; c[i][i] = 1;
}
for (int i = 2; i <= 10; i++)
for (int j = 1; j <= i-1; j++)
{
c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
}
}
void solve()
{
init();
for (int i = 1; i <= 10; i++)
{
for (int j = 1; j <= 10; j++)
{
if (i == 1 && j == 1)
continue;
cut[i][j] += cut[i - 1][j] + link[i - 1][j];
cut[i][j] %= mod;
for (int k = 0; k <= i - 1; k++)
{
for (int q = 1; q <= j; q++)
{
if (1 + k + q == i + j)continue;
ll num1 = c[i - 1][k]; ll num2 = c[j][q];
int l1 = k + 1; int r1 = q;
int l2 = i - 1 - k; int r2 = j - q;
cut[i][j] += num1*num2 % mod*link[l1][r1]%mod* ((link[l2][r2] + cut[l2][r2]) % mod);
cut[i][j] %= mod;
}
}
ll all = fact[i*j];
link[i][j] = all - cut[i][j];
link[i][j] = (link[i][j] + mod) % mod;
}
}
}
int main()
{
solve();
int n, m;
while (scanf("%d%d", &n, &m)!=EOF)
{
printf("%lld\n", link[n][m]);
}
return 0;
}