我居然没看题解瞎搞出来了?

题解:


不难想到找到每次减1的位置,然后减去它对最终答案的贡献。

假设有一个地方是\(x,1(mod~k)\)

那么减了1后就变成了\(x,0\)。

然后可以推到\(x,0,x,x\)。

可以看做以\(x,x\)为开头,做新的序列。

设y为x在mod k意义下的逆元,那么下次1的地方就是原斐波拉契序列中第一次出现y的位置。

如果没有逆元那就结束了。

原斐波拉契序列的非循环长度是\(O(k)\)级别的,这个可以通过随机序列第一次出现相同元素来理解,为在mo意义下应该是可以看做随机的,值域大小是\(O(k^2)\),那么期望长度就是\(O(\sqrt {k^2})\)。

所以斐波拉契序列做个3k左右然后预处理每一个元素第一次出现的位置。

然后x也是有循环的,因为x<k,所以肯定是\(O(k)\)的,那么就可以一起做了。

最后就是个等比矩阵求和,由于不一定有逆,所以要用分治法去求等比矩阵和。

由于循环开始之前和最后结尾的地方都要判,所以有点复杂。

Code:


#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, B = y; i <= B; i ++)
#define ff(i, x, y) for(int i = x, B = y; i < B; i ++)
#define fd(i, x, y) for(int i = x, B = y; i >= B; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std; ll n; int k, mo; const int N = 3e6 + 5; int f[N], fi[N];
int p[N], p0, us[N]; int gcd(int x, int y) { return (!y ? x : gcd(y, x % y));}
void exgcd(int a, int b, int &x, int &y) {
if(!b) {x = a, y = 0; return;}
exgcd(b, a % b, y, x); y -= (a / b) * x;
}
int qni(int p, int q) {
int x, y;
exgcd(p, q, x, y);
x = (x % q + q) % q;
return x;
} int l = -1, r; void work() {
int x = 1;
while(1) {
if(gcd(x, k) > 1) break;
int y = qni(x, k);
if(fi[y]) {
p0 ++;
p[p0] = p[p0 - 1] + fi[y];
if(us[y]) {
l = us[y]; r = p0;
break;
}
us[y] = p0;
x = (ll) f[fi[y] - 1] * x % k;
} else break;
}
} struct jz {
ll a[2][2];
jz() {
a[0][0] = a[1][1] = 1;
a[0][1] = a[1][0] = 0;
}
}; jz operator * (jz a, jz b) {
jz c;
fo(i, 0, 1) fo(j, 0, 1)
c.a[i][j] = (a.a[i][0] * b.a[0][j] + a.a[i][1] * b.a[1][j]) % mo;
return c;
} jz operator + (jz a, jz b) {
fo(i, 0, 1) fo(j, 0, 1) a.a[i][j] = (a.a[i][j] + b.a[i][j]) % mo;
return a;
} jz operator - (jz a, jz b) {
fo(i, 0, 1) fo(j, 0, 1) a.a[i][j] = (a.a[i][j] - b.a[i][j] + mo) % mo;
return a;
} jz ksm(jz x, ll y) {
jz s;
for(; y; y /= 2, x = x * x)
if(y & 1) s = s * x;
return s;
} jz z; jz ksb(jz x, ll y) {
if(y == 1) return x;
if(y & 1) return ksb(x, y - 1) * x + x;
jz a = ksb(x, y / 2);
return a + a * ksm(x, y / 2);
}
jz calc(jz x, ll y) {
jz s = jz();
if(y > 0) s = s + ksb(x, y);
return s;
} int main() {
scanf("%lld %d %d", &n, &k, &mo);
f[1] = f[2] = 1;
fo(i, 3, 3000000) {
f[i] = (f[i - 2] + f[i - 1]) % k;
if(!fi[f[i]]) fi[f[i]] = i;
}
work();
z.a[0][1] = z.a[1][0] = z.a[1][1] = 1; z.a[0][0] = 0;
ll ans = ksm(z, n).a[1][0];
if(l == -1) {
fo(i, 1, p0) if(p[i] <= n)
ans = (ans - ksm(z, n - p[i]).a[1][1] + mo) % mo;
} else {
fo(i, 1, l) if(p[i] <= n)
ans = (ans - ksm(z, n - p[i]).a[1][1] + mo) % mo;
if(p[l] <= n) {
ll v = (n - p[l]) / (p[r] - p[l]);
fo(i, l + 1, r) {
ll n2 = (ll) p[i] + v * (p[r] - p[l]);
if(n2 <= n) ans = (ans - ksm(z, n - n2).a[1][1] + mo) % mo;
}
if(v > 0) {
ll st = v * (p[r] - p[l]) + p[l];
jz sa; memset(sa.a, 0, sizeof sa.a);
fo(i, l + 1, r) sa = sa + ksm(z, p[r] - p[i]);
jz c = ksm(z, n - st) * sa * calc(ksm(z, p[r] - p[l]), v - 1);
ans = (ans - c.a[1][1] + mo) % mo;
}
}
}
pp("%lld\n", ans);
}

【NOI2011】兔农(循环节)的更多相关文章

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

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

  2. BZOJ2432 [Noi2011]兔农

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

  3. 2432: [Noi2011]兔农 - BZOJ

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

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

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

  5. NOI2011 兔农

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

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

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

  7. BZOJ 2432 兔农

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

  8. HDU 5895 Mathematician QSC(矩阵乘法+循环节降幂+除法取模小技巧+快速幂)

    传送门:HDU 5895 Mathematician QSC 这是一篇很好的题解,我想讲的他基本都讲了http://blog.csdn.net/queuelovestack/article/detai ...

  9. hdu 2837 Calculation 指数循环节套路题

    Calculation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  10. HDU 3746 (KMP求最小循环节) Cyclic Nacklace

    题意: 给出一个字符串,要求在后面添加最少的字符是的新串是循环的,且至少有两个循环节.输出最少需要添加字符的个数. 分析: 假设所给字符串为p[0...l-1],其长度为l 有这样一个结论: 这个串的 ...

随机推荐

  1. js console对象

    js调试 根据信息的不同性质,console对象显示信息的方法,分别是一般信息console.log(),console.info().除错信息console.debug().警告提示console. ...

  2. 【InnoDB】体系结构

    一.概述: innodb的整个体系架构就是由多个内存块组成的缓冲池及多个后台线程构成.缓冲池缓存磁盘数据(解决cpu速度和磁盘速度的严重不匹配问题),后台进程保证缓存池和磁盘数据的一致性(读取.刷新) ...

  3. mysql数据库优化学习

    目的避免出现页面访问错误慢查询造成页面无法加载阻塞造成数据无法提交优化从sql及索引,数据库结构,系统配置,硬件 日志慢查询日志:   show variables like 'slow_query_ ...

  4. jdbc的连接数据库,使用PreparedStatement实现增删改查等接口

    首先是连接,关闭资源等数据库操作 将连接数据库,关闭资源封装在JDBCUtils里 package jdbc.utils; import java.sql.Connection; import jav ...

  5. SVN server 服务端修改端口号

    SVN server 服务端修改端口号 在实际使用中可能当安装svn server 服务后,发现与后续其他程序端口冲突, 1.打开svn server 管理界面->操作->properti ...

  6. AcWing 208. 开关问题 (高斯消元+状压)打卡

    有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开. 你的目 ...

  7. vuedraggable 实现拖动数据改变

    // 引入组件 import Draggable from 'vuedraggable' // 使用组件 DragList 为自己封装好的组件 注意一定要使用vue的sync <DragList ...

  8. Arrays(一)二次封装自己的数组

    一.二次封装自己的数组 目标:设计int 类型数组, (1)可以根据用户传入的容量设置数组的长度,如果用户未设置容量,默认设置长度为10(考虑使用多态性) (2)判断数组是否为空 (3)获取数组的容量 ...

  9. ANSI 标准C 还定义了如下几个宏

    ANSI 标准C 还定义了如下几个宏:_LINE_ 表示正在编译的文件的行号_FILE_ 表示正在编译的文件的名字预处理名称意义#define 宏定义#undef 撤销已定义过的宏名#include ...

  10. django 之模板层

    1. 模板语法之变量 格式:{{ 变量名 }} 句点符,深度查询(可以点到方法,不要加括号,只能是无参的方法) 代码 视图函数: from django.shortcuts import render ...