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

2476: 战场的数目

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

Description

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=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define P 987654321
struct Matrix{int a[4][4]; Matrix() {memset(a,0,sizeof(a));}}A,B;
int N,T;
Matrix operator * (Matrix A,Matrix B)
{
    Matrix C;
    for (int k=1; k<=3; k++)
        for (int i=1; i<=3; i++)
            if (A.a[i][k])
                for (int j=1; j<=3; 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=1; i<=3; i++) re.a[i][i]=1;
    for (int i=k; i; i>>=1,x=x*x) if (i&1) re=re*x;
    return re;
}
int main()
{    
    A.a[1][1]=3; A.a[1][2]=-1; A.a[1][3]=0;
    A.a[2][1]=1; A.a[2][2]=0; A.a[2][3]=0;
    A.a[3][1]=0; A.a[3][2]=1; A.a[3][3]=0;
    B.a[1][1]=13; B.a[2][1]=5; B.a[3][1]=0;
    while (1)
        {
            N=read(); if (!N) break;
            if (N&1 || N<8) {puts("0"); continue;}
            if (N==8) {puts("2"); continue;}
            if (N==10) {puts("9"); continue;}
            Matrix ANS; 
            ANS=A^(N/2-5); 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
",(ANS.a[1][1]-(N/2-1)+P)%P);
        }
    return 0;
}

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

原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5946930.html