战场的数目——湖南省第六届大学生计算机程序设计竞赛

战场由若干单位正方形积木组成。积木占据了连续的若干列,且图形周长等于它最小包围矩形的周长。假设战场的图形周长为p,一共有多少种可能的战场?

战场不能恰好为一个矩形。

例如,p<8时没有符合要求的战场,p=8时有2种战场:


p=10有9种战场:


要求输出方案总数模987654321的值。


Input
输入文件最多包含25组测试数据,每个数据仅包含一行,有一个整数p(1<=p<=109),表示战场的图形周长。p=0表示输入结束,你的程序不应当处理这一行。
Output
对于每组数据,输出仅一行,即满足条件的战场总数除以987654321的余数。
Sample Input
7
8
9
10
0
Sample Output
0
2
0
9

Hint

思路:

根据题目意思可以得知这是一道找规律求递推公式的题目,所以,需要从前往后推,凭空想象只能是浪费时间。

分析:

n=4      战场数目为1(图形即一个单位正方形)

n=6     战场数目为2(图形为两个单位正方形横着排列与竖着排列两种)

n=8     战场数目为5

奇数无解

以上数目都是包括组成图形为矩形的,最后要减去这些数目。

从以上题目给出的图和已经得出的结论尝试推导递推公式,周长为n的一个图形如果左边有一个单个的正方形,减去他就是周长n-2的一个图形,同理右边与最上方

也是这个道理,而左右各减去一个正方形即为周长n-4的图形  这里发现n-4的图形包括在n-2中重复了,故递推公式为  f(n)=3*f(n-2)-f(n-4);

现在的问题是如何求得f(n).

这里用到了斐波拉契数列进行辅助。

在Fibonacci整数序列中,对于n≥20 = 0,1 = 1,n = n -1 + n -2。例如,斐波那契序列的前十项是:

0,1,1,2,3,5,8,13,21,34,...

斐波纳契序列的另一个公式是

求斐波拉契数列的f(n)我们用到了矩阵快速幂,而这道题同样的也可以运用 矩阵快速幂进行求解,所以可以得知公式f的第一项为n=4, 后面的随即求出。然后如何把这个

递推公式与斐波拉契数列的递推公式联系起来,思考与观察后可以猜测f(n)(n>=4)=F(n-4+1),所以可以先令m=n-4,求得pow(m),然后返回F(m+1)即为所求的的值KK1(

没有减去组成恰好为一个矩形的个数),所以还要求出一个公式为n/2-1,代表的是边长为n的矩形由1*1的正方形组成总共有多少种组成方法,所以最后所求的答案为

KK1-n/2+1.

PS:第一道矩阵非常规题,自己还差的太远,想了五个小时,一开始思路就错了,难点在于斐波拉契数列 的联系。吸取教训,继续。

#include<stdio.h>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long
#define mod 987654321
struct ju
{
    ll m[2][2];
}ans,base;
ju multi(ju a,ju b)
{
    ll temp[2][2];
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            temp[i][j]=a.m[i][j];
        }

    }
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            a.m[i][j]=0;
            for(int w=0;w<2;w++)
                a.m[i][j]+=(temp[i][w]*b.m[j][w])%mod;
        }
    }
    return a;
}
int pow(int n)
{
    base.m[0][0] = base.m[0][1] = base.m[1][0] = 1;
    base.m[1][1] = 0;
    ans.m[0][0] = ans.m[1][1] = 1;  // ans 初始化为单位矩阵
    ans.m[0][1] = ans.m[1][0] = 0;
    //n-=4;
    while(n)
    {
        if(n&1)
        {
            ans=multi(base,ans);
        }
        base=multi(base,base);
        n>>=1;
    }
    return ans.m[0][0];
}
int main()
{
    int n;
    while(scanf("%d",&n)&&n!=0)
    {
        if(n<8||n&1) printf("0\n");
        else
        {
            ll kk1=(pow(n-4)-n/2+1)%mod;
            if(kk1<0) kk1+=mod;
            printf("%lld\n",kk1%mod);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值