【BZOJ-2476】战场的数目 矩阵乘法 + 递推

时间:2023-03-09 02:35:32
【BZOJ-2476】战场的数目     矩阵乘法 + 递推

2476: 战场的数目

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 58  Solved: 38
[Submit][Status][Discuss]

Description

【BZOJ-2476】战场的数目     矩阵乘法 + 递推

Input

输入文件最多包含25组测试数据,每个数据仅包含一行,有一个整数p(1<=p<=109),表示战场的图形周长。p=0表示输入结束,你的程序不应当处理这一行。

Output

对于每组数据,输出仅一行,即满足条件的战场总数除以987654321的余数。

Sample Input

7
8
9
10
0

Sample Output

0
2
0
9

HINT

湖南省第六届大学生计算机程序设计竞赛

Source

鸣谢刘汝佳先生授权使用

Solution

战场的定义比较趋近于俄罗斯方块,及不能有悬空的块;每个块下面必须为底或者是另一个块,并且这些块不能构成一个矩形。

直接入手比较麻烦。

很显然的就是高就是最高的块所在的层数,宽也是最后一层的宽度。

分情况讨论一下,显然只有$p$为偶数的时候才可能有答案,奇数一定无解。

1.如果左/右只有一边存在一个单独的块。

这样删掉这个块将会得到周长为$p-2$时的一种方案。

2.如果左/右都不存在单独一个块。

这样无法通过删单个块得到一种周长为$p-2$的方案,但是,可以通过去掉最后一层得到一种周长为$p-2$的方案。

3.如果左/右都存在一个单独的块。

这种方案,会在情况1中额外统计,所以要减去,而这样的方案数则对应是周长为$p-4$时的方案。

然后就可以得到递推式$f[p]=2*f[p-2]+f[p-2]-f[p-4]=3*f[p-2]-f[p-4]$

然后这个显然是可以矩阵乘法优化的,不过这样会产生一些不必要的内存,所以我们把$p=p/2$然后就可以得到$f[p]=3*f[p-1]-f[p-2]$

这样得到的答案,是满足的情况,是包含正好组成矩形的情况的,所以我们在最后把它减去即可。

有一些小细节,稍微注意一下!

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
inline int read()
{
int x=,f=; char ch=getchar();
while (ch<'' || ch>'') {if (ch=='-') f=-; ch=getchar();}
while (ch>='' && ch<='') {x=x*+ch-''; ch=getchar();}
return x*f;
}
#define P 987654321
struct Matrix{int a[][]; Matrix() {memset(a,,sizeof(a));}}A,B;
int N,T;
Matrix operator * (Matrix A,Matrix B)
{
Matrix C;
for (int k=; k<=; k++)
for (int i=; i<=; i++)
if (A.a[i][k])
for (int j=; j<=; j++)
if (B.a[k][j])
C.a[i][j]=(C.a[i][j]+((LL)A.a[i][k]*B.a[k][j]+P)%P+P)%P;
return C;
}
Matrix operator ^ (Matrix x,int k)
{
Matrix re; for (int i=; i<=; i++) re.a[i][i]=;
for (int i=k; i; i>>=,x=x*x) if (i&) re=re*x;
return re;
}
int main()
{
A.a[][]=; A.a[][]=-; A.a[][]=;
A.a[][]=; A.a[][]=; A.a[][]=;
A.a[][]=; A.a[][]=; A.a[][]=;
B.a[][]=; B.a[][]=; B.a[][]=;
while ()
{
N=read(); if (!N) break;
if (N& || N<) {puts(""); continue;}
if (N==) {puts(""); continue;}
if (N==) {puts(""); continue;}
Matrix ANS;
ANS=A^(N/-); ANS=ANS*B;
// for (int i=1; i<=3; i++,puts(""))
// for (int j=1; j<=3; j++)
// printf("%d ",ANS.a[i][j]);
printf("%d\n",(ANS.a[][]-(N/-)+P)%P);
}
return ;
}

又发了道水题,很惭愧....