AK F.*ing leetcode 流浪计划之费马小定理与组合数取模

欢迎关注更多精彩
关注我,学习常用算法与数据结构,一题多解,降维打击。

费马小定理与证明

参考 https://zhuanlan.zhihu.com/p/594859227

费马小定理:如果p是一个质数,而正整数a不是p的倍数,那么a(p-1)≡1(mod p)。

证明:

引理1 S={1a, 2a, 3a, …, (p-1)a},S里所有数都不是p的倍数

由于 1到p-1都小于p, a也不能被p整除,说明a, 1到p-1 这些数都没有因子p。

由此引理1得证明。

引理2 S中所有元素对p取模不为0,且正好为集合 N = {1,2,3,…, p-1}

由引理1可知,对于任意小于p的两个不相同整数, i, j, ia%p != ja%p !=0。

假设 ia%p = ja%p,则 i%p * a%p = j%p * a%p, 得出i=j,与条件矛盾。

所以,对于S%p所有数字不相同,总个数为p-1个,由鸽巢原理可知,引理2正确。

根据引理2,可得到 ∏ S % p = ∏ N % p \displaystyle \prod_S \%p = \prod_N \%p S%p=N%p

两边整理得到 a p − 1 ( p − 1 ) ! % p = ( p − 1 ) ! % p a^{p-1}(p-1)!\%p=(p-1)!\%p ap1(p1)!%p=(p1)!%p

由于gcd(p, (p-1)!)=1, 得到 a(p-1)%p=1。

逆元

定义

已知整数a,p , a与p互质,如果存在一个数c<p使得 a*c%p=1, 则称c为a在模p下的逆元,记c=a-1 %p;
通俗理解就是求(1/a)%p。

用费马小定理求逆元

a(p-1)≡1(mod p)

可知 a*a(p-2)%p=1

从定义可知 a在模p下的逆元为 a(p-2)%p, 一般p是比较大的质数,可以使用快速幂求解。

typedef long long lld;lld MOD = 998244353;const int N = 1e6 + 10;// a的逆元为 fastmi(a, p-2)
lld fastmi(lld a, lld n) {lld ans = 1;while (n) {if (n & 1)ans = ans*a%MOD;a *= a;a %= MOD;n >>= 1;}return ans;
}

组合数取模

已经知p是一个比较大的质数(超过10的7次),求组合数C(n, m) , 0<=m<=n。

打表法

当n<=1000时,可以使用扬辉三角打表法。
用一个下三角矩阵存储组合数结果。
利用公式C[i][j] =( C[i - 1][j-1] + C[i - 1][j])%p,求解;
在这里插入图片描述


lld C[1010][1010];void initC() {C[0][0] = 1;C[1][0] = C[1][1] = 1;for (int i = 2; i < 1010; i++) {C[i][0] = C[i][i] = 1;for (int j = 1; j < i; ++j) C[i][j] =( C[i - 1][j-1] + C[i - 1][j])%MOD;}
}

逆元法

C n m = A n m m ! = n ! m ! ( n − m ) ! C_n^m=\frac{A_n^m}{m!} = \frac{n!}{m!(n-m)!} Cnm=m!Anm=m!(nm)!n!

从上式可以看出当m不是很大时,m!的逆元是可以在O(n)log§时间内求出来的。

当n不大时也可以直接迭代得到n!%p。

如果n-m不大也可以直接求得 A n m A_n^m%p Anm


lld inv[N];
// 计算n! 的逆元, inv[n]=(1/n!)%p = (n!)^(p-2)%p
void initInv() {lld sum = 1;inv[0] = 1;for (lld i = 1; i < N; ++i) {sum *= i;sum %= MOD;inv[i] = fastmi(sum, MOD - 2);}
}// 计算C(n, m) = n!/(n-m)!%p * inv[m]%p
lld C(lld n, lld m) {lld sum = 1;for (int i = n; i > (n - m); --i) sum = sum * i % MOD;return sum * inv[m] % MOD;
}

题目一

https://codeforces.com/contest/1967/problem/C

题目大意

题目基础相关知识:树状数组

定义lowbit(x) 是由x二进制最低位的1和后面的0组成的数字,例如,lowbit(12)=4, lowbit(8)=8.
取K值
arr是一个长度为n数组。

假如一个长度为n的数组sum 满足 s u m i = { ∑ j = i − l o w b i t ( i ) + 1 i a r r [ j ] } m o d 998244353 sum_i=\left \{\displaystyle \sum_{j=i-lowbit(i)+1}^{i}arr[j]\right \}mod\ 998244353 sumi= j=ilowbit(i)+1iarr[j] mod 998244353,则称sum为arr的二叉索引树。定义sum=f(arr)。

下图可以解释上述定义,红线为累加给上级关系。

树状数组示意图

给定一个数组a, 整数k,

f k ( a ) = { f ( a ) k = 1 f ( f k − 1 ( a ) ) k > 1 f^k(a)=\left \{\begin{array}{l}f(a) \ \ \ k=1\\f(f^{k-1}(a))\ \ \ k>1\end{array} \right . fk(a)={f(a)   k=1f(fk1(a))   k>1

问题:给定结果sum,k 求最初始的数组arr。
n 为arr长度, 1≤n≤2⋅105,1≤k≤109

问题解析

既然是累加关系,那么可以尝试找到累加系数关系。之后就可以使用高斯消元法求解每个未知数。

设c[i]为sumi的累加系数。

s u m i = { ∑ j = i − l o w b i t ( i ) + 1 i a r r [ j ] ∗ c [ i ] [ j ] } m o d 998244353 sum_i=\left \{\displaystyle \sum_{j=i-lowbit(i)+1}^{i}arr[j]*c[i][j]\right \}mod\ 998244353 sumi= j=ilowbit(i)+1iarr[j]c[i][j] mod 998244353

假设已经知道系数,我们可以从低到高枚举i, 然后把用sumi把相关联的上级减掉,剩下的sum数组就是答案。

下面来寻找系数与k的关系

手动模拟一下:

假设现在数组长度为8。
当k=1时,
系数c如下
在这里插入图片描述
横坐标为arr[i], 纵坐标为sum[i]。

k=2, 在上面基础上再模拟一遍
在这里插入图片描述
k=3,在上面基础上再模拟一遍
在这里插入图片描述

上面我们只看1,2,4,8行(从树上看是连续上级关系),

从上表观察发现每次K增加1,相应系数就是把k-1对应的系数全部加起来。
例如k=3时,c[8]1 = (c[8][1] + c[4][1]+c[2][1]+c[1][1])(k=2)

经过简单的分析可以得到以下一个表。

在这里插入图片描述

将上表命名为mat, 横坐标为k-1, 纵坐标为当前数字与目标数字的层数相。
可以看出下行就是上一行的前缀和。

至此,我们已经找到了k与系数的关系,但是k太大了,直接计算或者打表都不可能。

对上表进行旋转,可以得到扬辉三角,

在这里插入图片描述
也就是说mat[i][j] 与组合数C(n, m)存在一个关系。
通过线性变换可以得到 mat[i][j] = C(i+j, i)

实现细节

根据上面的推导

  • sum[1] = arr[1];
  • sum[2] = c[1][1]*arr[1] + arr[2];
  • sum[3] = arr[3];
  • sum[4] = c[4][1]*arr[1] + c[4][2]*arr[2] + c[4][3]*arr[3] + arr[4];
  • sum[5] = arr[5];
  • sum[6] = c[6][5]*arr[5] + arr[6];
  • sum[7] = arr[7];
  • sum[8] = c[8][1]*arr[1] + c[8][2]*arr[2] + c[8][3]*arr[3] +c[8][4]*arr[4] + c[8][5]*arr[5] + c[8][6]*arr[6] + c[8][7]*arr[7] + arr[8];

d = dep[i]-dep[j], 在二叉树上的深度
c [ i ] [ j ] = m a t [ d ] [ k − 1 ] = C ( d + k − 1 , d ) = i n v [ d ] ∗ A d + k − 1 d c[i][j] = mat[d][k-1]=C(d+k-1, d) = inv[d]*A_{d+k-1}^d c[i][j]=mat[d][k1]=C(d+k1,d)=inv[d]Ad+k1d

可从i=1开始 用c[][]*sum[i] 去减掉所有上级树上加成项。
相当于d每次会加1,
m a t [ d + 1 ] [ k − 1 ] = C ( d + 1 + k − 1 , d + 1 ) = i n v [ d + 1 ] ∗ A d + 1 + k − 1 d + 1 mat[d+1][k-1]=C(d+1+k-1, d+1) = inv[d+1]*A_{d+1+k-1}^{d+1} mat[d+1][k1]=C(d+1+k1,d+1)=inv[d+1]Ad+1+k1d+1
inv已经求好了。

A d + 1 + k − 1 d + 1 = A d + k − 1 d ∗ ( d + 1 + k − 1 ) A_{d+1+k-1}^{d+1}=A_{d+k-1}^d * (d+1+k-1) Ad+1+k1d+1=Ad+k1d(d+1+k1)
根据上述递推式,可以一边递推一边减掉加成项。
这样可以平均O(1)求出系数c.

代码

#include <iostream>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <cmath>using namespace std;typedef long long lld;lld MOD = 998244353;const int N = 1e6 + 10;lld fastmi(lld a, lld n) {lld ans = 1;while (n) {if (n & 1)ans = ans * a % MOD;a *= a;a %= MOD;n >>= 1;}return ans;
}lld inv[N];void initInv() {lld sum = 1;inv[0] = 1;for (lld i = 1; i < N; ++i) {sum *= i;sum %= MOD;inv[i] = fastmi(sum, MOD - 2);// if (i < 10)cout <<sum<<", "<< inv[i] << ", " << (inv[i]*sum%MOD) << endl;}
}int lowbit(int x) {return x & (-x);
}lld arr[N];lld C[1010][1010];void initC() {C[0][0] = 1;C[1][0] = C[1][1] = 1;for (int i = 2; i < 1010; i++) {C[i][0] = C[i][i] = 1;for (int j = 1; j < i; ++j) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;}
}void solve() {initInv();initC();int t;cin >> t;while (t--) {lld n, k;cin >> n >> k;for (int i = 1; i <= n; ++i)cin >> arr[i];for (int i = 1; i <= n; ++i) {lld isum = 1;for (int j = i + lowbit(i), d = 1; j <= n; j += lowbit(j), d++) {isum = isum * ((k - 1 + d) % MOD) % MOD;//cout << j<<", "<< d<<", "<< isum * inv[d] % MOD<<endl;arr[j] -= isum * arr[i] % MOD * inv[d] % MOD;arr[j] %= MOD;//cout << "mat" << k - 1 << ", " << d << endl;//cout << "C" << k - 1 +d<< ", " << d<< "=" << isum * inv[d] % MOD<< endl;/*arr[j] -= C[k-1+d][d] * arr[i] % MOD;arr[j] %= MOD;*///cout << "C" << k - 1 + d << ", " << d << "=" << C[k - 1 + d][d] << endl;}}for (int i = 1; i <= n; ++i) {if (arr[i] < 0)arr[i] += MOD;cout << arr[i] << " ";}cout << endl;}
}int main() {solve();return 0;
}
/*
2
8 100
1 2 1 4 1 2 1 8
6 7
1 4 3 17 5 162
8 1
1 2 1 4 1 2 1 8
6 2
1 4 3 17 5 16*/lld CC(lld n, lld m) {lld sum = 1;for (int i = n; i > (n - m); --i) sum = sum * i % MOD;return sum * inv[m] % MOD;
}

本人码农,希望通过自己的分享,让大家更容易学懂计算机知识。创作不易,帮忙点击公众号的链接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/339323.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW齿轮调制故障检测系统

LabVIEW齿轮调制故障检测系统 概述 开发了一种基于LabVIEW平台的齿轮调制故障检测系统&#xff0c;实现齿轮在恶劣工作条件下的故障振动信号的实时在线检测。系统利用LabVIEW的强大图形编程能力&#xff0c;结合Hilbert包络解调技术&#xff0c;对齿轮的振动信号进行精确分析…

opensips 3.5的DB部署

opensips 3.X的DB部署方式较之前版本有很大的不同。本文以opensips 3.5 为例&#xff0c;说明部署的过程。 当OpenSIPS安装完成后&#xff0c;需要进一步做什么&#xff1f;最大的可能就是部署配套的DB。因为很多功能离不开它&#xff0c;比如用户鉴权、注册信息持久化、dialog…

MySQL学习——影响选项文件处理的命令行选项和程序选项修改器

大多数支持选项文件的MySQL程序都处理以下选项。因为这些选项会影响选项文件的处理&#xff0c;所以必须在命令行上给出&#xff0c;而不是在选项文件中给出。为了正常工作&#xff0c;这些选项中的每一个都必须先于其他选项给出&#xff0c;但以下情况除外&#xff1a; -prin…

OpenCASCADE开发指南<十四>:OCCT建模类之BRepPrimAPI_MakePipe创建管道

1、OpenCasCade拓扑几何 在Open CASCADE Technology (OCCT) 中,除了基本三维几何体建模类BRepBuilderAPI外,还提供了复杂模型的建模类,常用的有如下几种,他们可以单独使用或相互组合,通过OCCT提供的融合函数进行组装。例如:BRepOffsetAPI_ThruSections、BRepOffsetAPI_Ma…

sqlite基本操作

简介 文章目录 简介1.数据库的安装2.数据库命令&#xff1a;API&#xff0c;创建表单代码 csprintf&#xff08;&#xff09;getchar和scanf&#xff08;&#xff09; 1.数据库的安装 sudo dpkg -i *.deb这个报错表明出现依赖问题 用这个命令后再试试sudo apt --fix-broken in…

Docker是什么?使用场景作用及Docker的安装和启动详解

目录 Docker是什么&#xff1f; Docker的发展 Docker的安装 Docker使用 Docker的运行机制 第一个Docker容器 进入Docker容器 客户机访问容器 Docker是什么&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker …

ChatGPT的基本原理是什么?又该如何提高其准确性?

在深入探索如何提升ChatGPT的准确性之前&#xff0c;让我们先来了解一下它的工作原理吧。ChatGPT是一种基于深度学习的自然语言生成模型&#xff0c;它通过预训练和微调两个关键步骤来学习和理解自然语言。 在预训练阶段&#xff0c;ChatGPT会接触到大规模的文本数据集&#x…

绘画参数配置及使用

绘画参数配置及使用 路径&#xff1a;站点后台-功能-AI绘画 进入参数配置 接口选择&#xff1a;多种接口自主选择&#xff08;需自己准备key&#xff09;&#xff0c;对应接口的key对话和绘画通用 存储空间&#xff1a; 位置在超管后台-存储空间 自主选择存储&#xff08;需…

冯喜运:6.3周一黄金原油行情分析及操作建议

【黄金消息面分析】&#xff1a;上周行情概述&#xff1a;现货黄金上周&#xff08;0527-0531&#xff09;反弹上探&#xff0c;5月27号开盘前本人曾提醒关注反弹&#xff0c;较当时上涨约30美元&#xff0c;最高至2364一线&#xff0c;其后震荡下跌。周线小幅收跌0.27%&#x…

微服务:Rabbitmq的WorkQueue模型的使用、默认消费方式(消息队列中间件)

文章目录 WorkQueue模型控制预取消息个数 WorkQueue模型 当然&#xff0c;一个队列&#xff0c;可以由多个消费者去监听。 来实现一下. 生产者&#xff1a; Testpublic void testWorkQueue() throws InterruptedException {// 队列名称String queueName "simple.queue…

通过提示工程将化学知识整合到大型语言模型中

在当今快速发展的人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;正成为科学研究的新兴工具。这些模型以其卓越的语言处理能力和零样本推理而闻名&#xff0c;为解决传统科学问题提供了全新的途径。然而&#xff0c;LLMs在特定科学领域的应用面临挑战&#…

力扣173题:二叉搜索树迭代器(含模拟面试)

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 关注微信公众号 数据分析螺丝钉 免费领取价值万元的python/java/商业…

蓝奏管理器iapp源码V3

蓝奏登录注册&#xff0c;简单管理文件夹等都没问题&#xff0c;就是上传接口需要有能力的人抓包进行修复一下&#xff08;我留了之前还能正常使用的接口&#xff0c;也是蓝奏官方的&#xff0c;所以参照一下就行。&#xff09;&#xff0c;这个应该也不是什么大问题&#xff0…

【自然语言处理】【Scaling Law】Observational Scaling Laws:跨不同模型构建Scaling Law

相关博客 【自然语言处理】【Scaling Law】Observational Scaling Laws&#xff1a;跨不同模型构建Scaling Law 【自然语言处理】【Scaling Law】语言模型物理学 第3.3部分&#xff1a;知识容量Scaling Laws 【自然语言处理】Transformer中的一种线性特征 【自然语言处理】【大…

Ansible04-Ansible Vars变量详解

目录 写在前面6 Ansible Vars 变量6.1 playbook中的变量6.1.1 playbook中定义变量的格式6.1.2 举例6.1.3 小tip 6.2 共有变量6.2.1 变量文件6.2.1.1 变量文件编写6.2.1.2 playbook编写6.2.1.3 运行测试 6.2.2 根据主机组使用变量6.2.2.1 groups_vars编写6.2.2.2 playbook编写6.…

第17篇:JTAG UART IP应用<四>

Q&#xff1a;如何通过JTAG UART发送命令控制开发板的外设比如LED&#xff1f; A&#xff1a;Quartus硬件工程以及Platform Designer系统在第一个Nios II工程--Hello_World的Quartus硬件工程基础上添加PIO&#xff0c;表示DE2-115开发板上的18个红色LED。 Nios II软件工程对应…

mysql中EXPLAIN详解

大家好。众所周知&#xff0c;MySQL 查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划&#xff0c;这个执行计划展示了接下来具体执行查询的方式。在日常工作过程中&#xff0c;我们可以使用EXPLAIN语句来查看某个查询语句的具体执行计划&#xff0c; 今天我们…

JMeter的基本使用

JMeter的基本使用三步骤&#xff1a;1.添加线程、2.添加请求、3.添加查询结果的内容 如果需要添加token请求头来验证&#xff0c;则需要再加上一步骤&#xff1a;添加请求头 1.线程 添加线程的方式 主要修改者三个属性值 Number of Threads&#xff1a;并发线程数 Ramp-up…

LabVIEW通过以太网控制PLC程序开发

在使用LabVIEW通过以太网控制PLC程序开发时&#xff0c;需要综合考虑硬件、软件和通信协议的协调工作。以下是详细步骤、注意事项、重点和难点分析&#xff0c;以及几种实现方式及其特点的概述。 实现步骤 确定硬件和软件环境&#xff1a; 确定PLC型号和品牌&#xff08;如西门…

Java 18新特性深度解析:提升开发效率与性能的革新工具

在Java的世界中&#xff0c;每一次更新都带来新的惊喜和挑战。Java 18作为长期支持版本&#xff0c;不仅延续了Java语言的稳定性和可靠性&#xff0c;还引入了一系列令人兴奋的新特性&#xff0c;旨在进一步提升开发者的生产力和应用程序的性能。本文将深入探讨Java 18中的关键…