@hdu - 5729@ Rigid Frameworks


@description@

如果对于一个平面图,将边看成火柴棍,将点看成用橡皮筋将木棍的头绑在一起(请自行脑补)。如果这个平面图不能够改变形状,称这个平面图为刚体图。

如下图中只有 D 不是刚体图。
题目描述用1

给定一个 n*m 的方格图,你可以在某些方格的对角线上加支撑木棍以保持它的形状不变。

问让一个 n*m 的方格图变为刚体图,添加支撑木棍的方案数是多少?

下图展现了一个 2*3 的方格图以及它不是刚体图的原因。
题目描述用2

下图展现了一个让 2*3 的方格图变为刚体图的方案:
题目描述用3

input
多组数据。每组数据只包含一行两个整数 n, m 描述方格图的大小

output
对于每组数据,输出方案数 mod 10^9 + 7。

sample input
1 2
3 2
7 9
10 10
sample output
4
448
357533852
935300639

@solution@

自我脑补一下,可以发现一个正方形将它拖过来拖过去,也只能变成平行四边形。
即:无论怎么变化,它的对边始终平行。
相当于每一行的竖边的相对位置关系、每一列的横边的相对位置关系是确定的(即平行)。

考虑在一个正方形内加支撑,则它的横边与竖边就变为垂直的了。
又因为平行具有传递性,会将垂直关系传递。所以这个正方形所在的行的每一条竖边与所在的列的每一条横边垂直。
相当于一个正方形内加支撑,它所在的行与列就有了相对的位置关系。

不难发现方格图是刚体图与它所有的行与列互相之间都有相对的位置关系是等价的。
于是我们考虑以行、列建二分图,则相当于询问该二分图连通的方案数。

求连通方案很显然是容斥,即用总方案数减去不连通的数量。同时因为是二分图,所以可以不使用状压 dp 来做。
定义 dp[i][j] 表示左边 i 个点与右边 j 个点连通的方案数。
因为边有两类(对角线有两种),总方案数为 3^(i*j)。
再考虑不连通时与左边的点 1 连通的连通块,可得状态转移(注意左边的点 1 必须要选):
\[dp[i][j] = 3^{i*j}-\sum_{p\le i,q\le j}3^{(i-p)*(j-q)}*dp[p][q]*C_{i-1}^{p-1}*C_{j}^{q}\]

然后 O(n^4) 做个 dp 即可

@accepted code@

#include<cstdio>
const int MOD = int(1E9) + 7;
const int MAXN = 10;
int dp[MAXN + 5][MAXN + 5], comb[MAXN + 5][MAXN + 5], pw[MAXN*MAXN + 5];
void init() {
    for(int i=0;i<=MAXN;i++) {
        comb[i][0] = 1;
        for(int j=1;j<=i;j++)
            comb[i][j] = (comb[i-1][j] + comb[i-1][j-1])%MOD;
    }
    pw[0] = 1;
    for(int i=1;i<=MAXN*MAXN;i++)
        pw[i] = 3LL*pw[i-1]%MOD;
    for(int i=0;i<=MAXN;i++)
        for(int j=0;j<=MAXN;j++)
            dp[i][j] = pw[i*j];
    for(int i=0;i<=MAXN;i++)
        for(int j=0;j<=MAXN;j++)
            for(int l=0;l<=i-1;l++)
                for(int k=0;k<=j;k++) {
                    if( l == i-1 && k == j ) continue;
                    dp[i][j] = (dp[i][j] + MOD - 1LL*pw[(i-l-1)*(j-k)]*dp[l+1][k]%MOD*comb[i-1][l]%MOD*comb[j][k]%MOD)%MOD;
                }
}
int main() {
    int n, m; init(); 
    while( scanf("%d%d", &n, &m) == 2 )
        printf("%d\n", dp[n][m]);
}

@details@

刚体。。。说实话我还真不知道是啥。。。
所以题意的理解卡了很久。。。

然后看到 n, m 这么小想可不可以写一个插头 dp 啥的,写着写着把自己 hack 掉了。。。
你告诉 n, m <= 10 然后跑一个 O(n^4) 的算法?这个数据范围。。。是用来误导人的嘛。。。

不过这个题的建模还是挺有趣。

转载于:https://www.cnblogs.com/Tiw-Air-OAO/p/11122815.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值