czy的后宫

Problem 3 czy的后宫(harem.cpp/c/pas)

【题目描述】

czy要妥善安排他的后宫,他想在机房摆一群妹子,一共有n个位置排成一排,每个位置可以摆妹子也可以不摆妹子。有些类型妹子如果摆在相邻的位置(隔着一个空的位置不算相邻),就不好看了。假定每种妹子数量无限,求摆妹子的方案数。

【输入格式】

输入有m+1行,第一行有两个用空格隔开的正整数n、m,m表示妹子的种类数。接下来的m行,每行有m个字符1或0,若第i行第j列为1,则表示第i种妹子第j种妹子不能排在相邻的位置,输入保证对称。(提示:同一种妹子可能不能排在相邻位置)。

【输出格式】

输出只有一个整数,为方案数(这个数字可能很大,请输出方案数除以1000000007的余数。

【样例输入】

2 2

01

10

【样例输出】

7

【样例说明】

七种方案为(空,空)、(空,1)、(1、空)、(2、空)、(空、2)、(1,1)、(2,2)。

【数据范围】

20%的数据,1<n≤5,0<m≤10。

60%的数据,1<n≤200,0<m≤100。

100%的数据,1<n≤1000000000,0<m≤100。

【题解】

  题目很有意思。。

  这么晚了不详细写了,反正就是矩阵乘法加速递推

  可以把不摆妹子的情况看成一种特殊的妹子,叫做“空妹子”

  f[i][j]表示考虑了前i个妹子,最后一个妹子是j,那么f[i][j]=∑f[i-1][each k 满足map[k][j]==0],我们把map里的值全都取反(0变1,1变0),那么f[i][j]=∑f[i-1][each k 满足map[k][j]==0]。

  把i这维去掉比较好看f[j]=∑f[k] (map[k][j]==1)

  也就是f[j]=∑f[k]*map[k][j]

  构建矩阵{f[1],f[2],...,f[m]}

  把它乘上map正好得到下一个{f[1],f[2],...,f[m]}

  矩阵乘法快速幂,时间复杂度O(lb n * M^3)

代码

#include <cstdio>
#include <cstring>
#define ll long long
#define p 1000000007
#define maxm 110
using namespace std;
ll n, m;
struct matrix
{
	int m[maxm][maxm], t;
	ll r ,c;
	matrix(){memset(m,0,sizeof(m));}
	matrix(ll x, ll y){r=x;c=y;memset(m,0,sizeof(m));}
	matrix operator*(matrix t)
	{
		matrix ans(r,t.c);
		ll i, j, k;
		for(i=1;i<=r;i++)
			for(j=1;j<=t.c;j++)
				for(k=1;k<=c;k++)
					ans.m[i][j]=int(( (ll)ans.m[i][j] + (ll)m[i][k]*(ll)t.m[k][j] ) %p);
		return ans;
	}
	void show()
	{
		ll i, j;
		for(i=1;i<=r;i++)
		{
			for(j=1;j<=c;j++)printf("%d ",m[i][j]);printf("\n");
		}
	}
};
matrix pow(matrix a, ll b)
{
	matrix t, ans(a.r,a.r);
	ll i, j;
	for(i=1;i<=ans.r;i++)ans.m[i][i]=1;
	for(t=a;b;b>>=1,t=t*t)if(b&1)ans=ans*t;
	return ans;
}
int main()
{
	freopen("harem.in","r",stdin);
	freopen("harem.out","w",stdout);
	ll i, j, ans;
	char s[150];
	scanf("%I64d%I64d",&n,&m);
	matrix a(1,m+1), t(m+1,m+1);
	for(i=1;i<=m;i++)
	{
		scanf("%s",s+1);
		for(j=1;j<=m;j++)t.m[i][j]=!(s[j]-48);
		t.m[i][m+1]=1;
	}
	for(i=1;i<=m+1;i++)t.m[m+1][i]=1;
	m++;
	for(i=1;i<=m;i++)a.m[1][i]=1;
	t=pow(t,n-1);
	a=a*t;
	for(i=1,ans=0;i<=m;i++)ans=(ans+a.m[1][i])%p;
	printf("%I64d\n",ans);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值