hdu 5434 Peace small elephant 状压 快速幂

问题描述
小明很喜欢国际象棋,尤其喜欢国际象棋里面的大象(只要无阻挡能够斜着走任意格),但是他觉得国际象棋里的大象太凶残了,于是他想到了小象,
小象就没有大象那么凶残,它的攻击范围是它当前格子直角所斜对的格子。现在小明要在棋盘上放很多个小象,有趣的是,当两个小象所在格子有公共边时,
它们将合体变成合体象,多个小象满足条件也会合体,合体象的攻击范围也是它所覆盖格子区域直角所斜对的格子,现在要求任何一个象的攻击范围上是空的(即不摆放棋子),
小明的棋盘很特殊,有mnmn个格子,求满足条件的摆放的方案数,由于方案数太大,需要对10000000071000000007取模。
下面给出几种形状下的象的攻击范围图,叉号表示攻击范围。

输入描述
输入有多组数据(最多55组),每组数据有两个整数nmn,m含义如题目描述。
1m71n10000000001m7,1n1000000000
输出描述
每组数据对应输出一行包含一个整数,表示满足条件的摆放的方案数。
输入样例
1 1
2 3
输出样例
2
50
因为m比较小,可以对列进行状压,用一个数字代表一个列的状态,然后如果可以从状态i转移到状态j,那么添加一条i道到j的边,题目要求的是n列有多少种方法,其实就是问建好的图中有多少种路径长度为n-1的走法,这个可以由最开始构造的可达矩阵乘方n-1次得到,将最后的矩阵求和一边就好。
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long ll;
const ll mod = 1000000007;
const int maxn = 130;
int n, m;

struct mat
{
	int num[maxn][maxn];
	mat()
	{
		memset(num, 0, sizeof(mat));
	}
	mat operator * (const mat& c)const
	{
		mat ans;
		int col = 1 << m;
		for (int i = 0; i < col; i++)
			for (int k = 0; k < col; k++)
			{
				if (num[i][k] == 0) continue;
				for (int j = 0; j < col; j++)
					ans.num[i][j] = (ans.num[i][j] + (ll)num[i][k] * c.num[k][j] % mod) % mod;
			}
		return ans;
	}
};

mat pow(mat x, int k)
{
	mat ans;
	for (int i = 0; i < (1 << m); i++) ans.num[i][i] = 1;
	while (k)
	{
		if (k & 1) ans = ans * x;
		x = x * x;
		k >>= 1;
	}
	return ans;
}

int ok(int s1, int s2)
{
	for (int i = 0; i < m; i++)
	{
		if (s2 & (1 << i))
		{
			if ((1 << i) & s1) continue;
			if (i && !(s2 & (1 << (i - 1))) && (s1 & (1 << (i - 1)))) return 0;
			if (i != (m - 1) && !(s2 & (1 << (i + 1))) && (s1 & (1 << (i + 1)))) return 0;
		}
	}
	return 1;
}
int main()
{
	while (scanf("%d%d", &n, &m) != EOF)
	{
		mat a;
		int col = 1 << m;
		for (int i = 0; i < col; i++)
			for (int j = 0; j < col; j++)
				a.num[i][j] = ok(i, j);
		a = pow(a, n - 1);
		int ans = 0;
		for (int i = 0; i < col; i++)
			for (int j = 0; j < col; j++)
			ans = (ans + a.num[i][j]) % mod;
		printf("%d\n", ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值