Description

农夫栋栋近年收入不景气,正在他发愁如何能多赚点钱时,他听到隔壁的小朋友在讨论兔子繁殖的问题。

问题是这样的:第一个月初有一对刚出生的小兔子,经过两个月长大后,这对兔子从第三个月开始,每个月初生一对小兔子。新出生的小兔子生长两个月后又能每个月生出一对小兔子。问第n个月有多少只兔子?

聪明的你可能已经发现,第\(n\)个月的兔子数正好是第\(n\)个\(Fibonacci\)(斐波那契)数。栋栋不懂什么是Fibonacci数,但他也发现了规律:第\(i+2\)个月的兔子数等于第\(i\)个月的兔子数加上第\(i+1\)个月的兔子数。前几个月的兔子数依次为:

\(1\;1\;2\;3\;5\;8\;13\;21\;34 \cdots\)

栋栋发现越到后面兔子数增长的越快,期待养兔子一定能赚大钱,于是栋栋在第一个月初买了一对小兔子开始饲养。

每天,栋栋都要给兔子们喂食,兔子们吃食时非常特别,总是每k对兔子围成一圈,最后剩下的不足\(k\)对的围成一圈,由于兔子特别害怕孤独,从第三个月开始,如果吃食时围成某一个圈的只有一对兔子,这对兔子就会很快死掉。

我们假设死去的总是刚出生的兔子,那么每个月的兔子数仍然是可以计算的。例如,当\(k=7\)时,前几个月的兔子数依次为:

\(1\;1\;2\;3\;5\;7\;12\;19\;31\;49\;80 \cdots\)

给定\(n\),你能帮助栋栋计算第\(n\)个月他有多少对兔子么?由于答案可能非常大,你只需要告诉栋栋第\(n\)个月的兔子对数除\(p\)的余数即可。

Input

输入一行,包含三个正整数\(n, k, p\)。

Output

输出一行,包含一个整数,表示栋栋第\(n\)个月的兔子对数除p的余数。

Sample Input

6 7 100

Sample Output

7

HINT

\(1 \le N \le 10^{18}\)

\(2 \le K \le 10^{6}\)

\(2 \le P \le 10^9\)

一道很好的矩阵乘法的题目,综合性感觉蛮强的。

以\(k=7\)为例,考虑\(f[i]%k\)组成的序列:

1,1,2,3,5,0,

5,5,3,0,

3,3,6,2,0,

2,2,4,6,3,2,5,0,5,5,3,0,

3,3,6,2,0,

\(\cdots\)

把减\(1\)得\(0\)的位置标出,并以这些\(0\)为界分段,可以发现:

①每段开头必为相同两数,它恰是上一段的最末一位非\(0\)数;由于总共只有\(k-1\)种余数,所以不超过\(k\)段就会出现循环(如果有的话),比如上面\(k=7\)时的第\(3,4\)段就是循环节。

②记斐波那契数列为\(fib[i]\)。假如某段段首数字为\(x\),那么这一段内第i个数即为\(x \times fib[i]%k\)。若记这一段长度为\(len\),则有\(x \times fib[len] \equiv 1(mod k)\)。

现在我们试图找到整个数列的循环结构:根据上式,①求x的逆元得到\(fib[len]\),②由\(fib[len]\)得知\(len\),③用\(x \times fib[len-1]%k\)算出下一段的段首,重复操作直到发现循环(或者发现这一段永远不终止)。

至于具体实现:①扩欧或者欧拉定理②预处理\(indfib[y]\)数组,表示斐波那契数列中模\(k\)余\(y\)的数第一次出现的下标(开头的两个1不算)③预处理\(fib[i]\)模\(k\)的值。有一个结论:斐波那契数列模\(k\)后一定是0,1,1开头的纯循环,而且这个循环节的长度$ \le 6k\((不知具体怎么证。。),所以只需暴力算\)fib\(数组并同时记录\)indfib[]\(,发现循环即停止。
注意,假如第①步不存在逆元,或者第②步不存在符合的\)len$,那么这一段将永远不会终止(比如k=8时就是这样),那么整个数列就不存在循环了(可以视作最后一段的长度为无穷大)。

接下来考虑如何用矩阵乘法计算\(f[n]%p\)。

两个重要矩乘:

分别记这两个\(3 \times 3\)矩阵为\(A,B\)。令初始矩阵为,通过对其不断右乘\(A\)和\(B\)便能实现累加、减\(1\)两种操作。对于分出的每一段算出一个矩阵\(A^{len} \times B\),表示这一段的“效果”。

接下来是喜闻乐见的分类讨论时间:假如整个数列是循环的,判断第n项是否在混循环的那部分里,若是则直接把前面几段乘起来,n所在这一段的零头直接用A的次幂算;若不是则先把混循环全部乘起来,然后把循环节全部乘起来,算出循环次数再快速幂,然后再像刚才一样算零头乘上去。若数列不循环倒方便些,也与上面类似,不多说了;如果长度无限,直接矩乘累加即可。

上述内容引自http://jcvb.is-programmer.com/posts/39528.html

最后贴一份自己的代码:

#include<cstring>
#include<cstdio>
#include<cstdlib>
using namespace std; typedef long long ll;
#define maxn (1000010)
ll n,K,p,fib[10000010],len[maxn],indfib[maxn],inv[maxn];
bool vis[maxn]; struct Matrix
{
ll a,b; ll s[4][4];
Matrix () { memset(s,0,sizeof(s)); }
friend inline Matrix operator * (const Matrix &x,const Matrix &y)
{
Matrix z; z.a = x.a; z.b = y.b;
for (ll i = 1;i <= z.a;++i)
for (ll j = 1;j <= z.b;++j)
for (ll k = 1;k <= x.b;++k)
(z.s[i][j] += x.s[i][k]*y.s[k][j]%p)%=p;
return z;
}
}save[maxn]; inline ll exgcd(ll a,ll b,ll c)
{
if (a == 0) return -1;
else if (c % a == 0) return c/a;
ll t = exgcd(b % a,a,((-c % a)+a)%a);
if (t == -1) return -1;
return (t*b+c)/a;
} inline Matrix qsm(Matrix x,ll y)
{
Matrix ret;
ret.a = ret.b = x.a; ret.s[1][1] = ret.s[2][2] = ret.s[3][3] = 1;
for (;y;y >>= 1,x = x*x)
if (y & 1) ret = ret*x;
return ret;
} int main()
{
freopen("2432.in","r",stdin);
freopen("2432.out","w",stdout);
scanf("%lld %lld %lld",&n,&K,&p);
fib[1] = fib[2] = 1;
for (ll i = 3;;++i)
{
fib[i] = fib[i-1] + fib[i-2];
if (fib[i] >= K) fib[i] -= K;
if (!indfib[fib[i]]) indfib[fib[i]] = i;
if (fib[i] == 1&&fib[i-1] == 1) break;
}
Matrix ans,mul,dec; bool sign = false;
ans.a = 1; ans.b = 3; ans.s[1][1] = ans.s[1][3] = 1;
mul.a = mul.b = 3; mul.s[1][2] = mul.s[2][1] = mul.s[2][2] = mul.s[3][3] = 1;
dec.a = dec.b = 3; dec.s[1][1] = dec.s[2][2] = dec.s[3][3] = 1; dec.s[3][2] = -1;
for (ll t = 1;n;)
{
if (!inv[t]) inv[t] = exgcd(t,K,1);
if (inv[t] == -1) { ans = ans*qsm(mul,n); n = 0; }
else
{
if (!vis[t]||sign)
{
vis[t] = true;
if (!indfib[inv[t]])
{
ans = ans*qsm(mul,n); n = 0;
}
else
{
len[t] = indfib[inv[t]];
if (n >= len[t])
{
n -= len[t];
save[t] = qsm(mul,len[t])*dec;
ans = ans*save[t];
(t *= fib[len[t]-1])%=K;
}
else { ans = ans*qsm(mul,n); n = 0; }
}
}
else
{
ll cnt = 0; Matrix ret; ret.a = ret.b = 3; ret.s[1][1] = ret.s[2][2] = ret.s[3][3] = 1;
for (ll i = t*fib[len[t]-1]%K;i != t;(i *= fib[len[i]-1])%=K)
cnt += len[i],ret = ret * save[i];
cnt += len[t],ret = save[t]*ret;
ans = ans*qsm(ret,n/cnt); n %= cnt;
sign = true;
}
}
}
printf("%lld",(ans.s[1][2]+p)%p);
fclose(stdin); fclose(stdout);
return 0;
}

BZOJ 2432 兔农的更多相关文章

  1. [BZOJ2432][Noi2011]兔农 矩阵乘法+exgcd

    2432: [Noi2011]兔农 Time Limit: 10 Sec  Memory Limit: 256 MB Description 农夫栋栋近年收入不景气,正在他发愁如何能多赚点钱时,他听到 ...

  2. 【BZOJ2432】【NOI2011】兔农(数论,矩阵快速幂)

    [BZOJ2432][NOI2011]兔农(数论,矩阵快速幂) 题面 BZOJ 题解 这题\(75\)分就是送的,我什么都不想写. 先手玩一下,发现每次每次出现\(mod\ K=1\)的数之后 把它减 ...

  3. 2432: [Noi2011]兔农 - BZOJ

    Description 农夫栋栋近年收入不景气,正在他发愁如何能多赚点钱时,他听到隔壁的小朋友在讨论兔子繁殖的问题. 问题是这样的:第一个月初有一对刚出生的小兔子,经过两个月长大后,这对兔子从第三个月 ...

  4. 【BZOJ 2432】 [Noi2011]兔农 矩乘+数论

    这道题的暴力分还是很良心嘛~~~~~ 直接刚的话我发现本蒟蒻只会暴力,矩乘根本写不出来,然后让我们找一下规律,我们发现如果我们把这个序列在mod k的意义下摆出,并且在此过程中把值为1的的数减一,我们 ...

  5. BZOJ2432 [Noi2011]兔农

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...

  6. NOI2011 兔农

    http://www.lydsy.com/JudgeOnline/problem.php?id=2432 感觉是day1中最难的一题,还好出题人很良心,给了75分部分分. 还是跪拜策爷吧~Orz ht ...

  7. 【bzoj2432】【NOI2011】兔农

    题目描述 农夫栋栋近年收入不景气,正在他发愁如何能多赚点钱时,他听到隔壁的小 朋友在讨论兔子繁殖的问题. 问题是这样的:第一个月初有一对刚出生的小兔子,经过两个月长大后,这 对兔子从第三个月开始,每个 ...

  8. BZOJ 2339 卡农(组合数学)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2339 题意: 思路: i64 Pow(i64 a,i64 b,i64 mod){    ...

  9. NOI 2011 兔农 题解

    事先声明,本博客代码主要模仿accepoc,且仅针对一般如本博主一样的蒟蒻. 这道题不得不说数据良心,给了75分的水分,但剩下25分真心很难得到,因此我们就来讲一讲这剩下的25分. 首先,有数据可知他 ...

随机推荐

  1. Python基础知识--列表和集合

    列表:有序性,可以存放任意类型的对象,通过索引访问,可以分片操作 >>> L = ['id', 1000, 'scd', 1000, 'scd'] >>> L [' ...

  2. js获取键盘的keyCode-------Day42

    济南今天是大雨倾盆啊,这闷热一扫而空,只是有些电闪雷鸣的,原想在公司里就完毕今天的博客记录的,只是不知道为什么怎么也登不上博客,预计是CSDN当时的server出问题了吧,好在到了晚上,这雷声小了也少 ...

  3. 关于开发环境 git 重新部署

    apps  开发机器 多次因为升级出现无法登陆 下面就重新部署 流程做笔记 1   备份 根目录下的 那一堆shell 和 Cache/data 下的系统配置 2  shell : su www   ...

  4. android 41 Environment

    assets通常存储音频视频文件,但不要太大. Environment可以获取sd卡的相关信息,sd卡的根路径:/storage/sdcard activity.java package com.sx ...

  5. Windows7如何在安全模式下卸载驱动(亲测)

    在桌面“我的电脑”上点鼠标右键,选择“属性”,“硬件”,“设备管理器”,找到“显示卡选项”,打开前面的“+”,然后按鼠标右键,选择“卸载”就可以了. (亲测,主板驱动卸载成功启动)

  6. [转] 使用NVM快速搭建NODE开发环境

    export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node 本文主要介绍最近自己碰到的几个坑以及搜索到的相应解决方案: 如何快速搭 ...

  7. css 权威指南笔记( 六)-基本视觉格式化

    块级元素 百分数:边框的宽度不能是百分数,只能是长度.基本原则是只使用百分数将无法创建完全灵活的元素布局(即所有属性都可设置). 合并垂直外边距 相邻外边距会沿着竖轴合并.两个外边距中较小的一个会被较 ...

  8. codevs 1817 灾后重建

    /* 暴力暴力 离线每次添边 堆优化dij 70 SPFA 80..... */ #include<iostream> #include<cstdio> #include< ...

  9. Linux 查看系统硬件信息(实例详解)

    原文链接:http://www.cnblogs.com/ggjucheng/archive/2013/01/14/2859613.html linux查看系统的硬件信息,并不像windows那么直观, ...

  10. Asp.net Mvc4 基于Authorize实现的模块访问权限

    在MVC中,我们可以通过在action或者controller上设置Authorize[Role="xxx"] 的方式来设置用户对action的访问权限.显然,这样并不能满足我们的 ...