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 16

2
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;
}


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

继承的基本语法

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在编写类时&#xff0c;并不是每次都要从空白开始。当要编写的类和另一个已经存在的类之间存在一定的继承关系时&#xff0c;就可以通过继承来达到代…

AI早班车6.3

1.蚂蚁技术日&#xff1a;支付宝三大「AI 管家」亮相。 2.百度赵世奇&#xff1a;百度搜索&#xff0b;文心智能体平台&#xff0c;助力智能体人人可用。 3.腾讯&#xff1a;发布大模型App腾讯元宝。 4.AFAC2024金融智能创新大赛启动&#xff0c;让高质量金融服务人人可用 …

Docker笔记-解决非交互式运行python时print不输出的问题

换句话来说就是在docker中如何不会python的print 只需要在启动时&#xff0c;不让python缓冲其输出。 关键命令如下&#xff1a;PYTHONUNBUFFERED1 如下&#xff1a; docker run -e PYTHONUNBUFFERED1 <your_image> 下面解释下-e "-e"选项的全称是"…

lux和ffmpeg进行下载各大主流自媒体平台视频

1、lux下载&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1WjGbouL3KFTU6LeqZmACpA?pwdagpp 提取码&#xff1a;agpp 2、ffmpeg下载&#xff0c;跟lux放在同一个目录&#xff1b; 3、为lux、ffmpeg设置环境变量&#xff1b; 4、WINR&#xff0c;打开运行&#xff0…

Love-Yi情侣网站3.0存在SQL注入漏洞

目录 1. 前言 2. 网站简介 3. 寻找特征点 3.1 第一次尝试 3.2 第二次尝试 4.资产搜索 5.漏洞复现 5.1 寻找漏洞点 5.2 进行进一步测试 5.2.1 手动测试 1.寻找字段 2.寻找回显位 3.查询当前用户 5.2.2 sqlmap去跑 6.总结 1. 前言 朋友说自己建了一个情侣网站,看到…

chat4-Server端保存聊天消息到mysql

本文档描述了Server端接收到Client的消息并转发给所有客户端或私发给某个客户端 同时将聊天消息保存到mysql 服务端为当前客户端创建一个线程&#xff0c;此线程接收当前客户端的消息并转发给所有客户端或私发给某个客户端同时将聊天消息保存到mysql 本文档主要总结了将聊天…

基于django | 创建app,并启动django

1、删除系统默认的目录路径&#xff1a;BASE_DIR / templetes 2、在终端输入命令&#xff1a; python manage.py startapp app01 # 这里的app01是我创建app的名称 3、如果没有创建成功&#xff0c;手动点击 Creat App , 4、在 setting.py 中找到 INSTALLED_APPS ,添加 ap…

✅count(1)、count(*) 与 count(列名) 的区别

简单来说&#xff1a; COUNT(1) 和 COUNT(*) 表示的是直接查询符合条件的数据库表的行数。而 COUNT(列名) 表示的是查询符合条件的列的值不为 NULL 的行数。 除了查询得到结果集有区别之外&#xff0c;在性能方面 COUNT() 约等于 COUNT(1)&#xff0c;但是 **COUNT() 是 SQL9…

Qt——升级系列(Level Two):Hello Qt 程序实现、项目文件解析、

Hello Qt 程序实现 使用“按钮”实现 纯代码方式实现&#xff1a; // Widget构造函数的实现 Widget::Widget(QWidget *parent): QWidget(parent) // 使用父类构造函数初始化QWidget&#xff0c;传入父窗口指针, ui(new Ui::Widget) // 创建Ui::Widget类的实例&#xff0c;并…

基于GTX 8B10B编码的自定义PHY接收模块(高速收发器十三)

点击进入高速收发器系列文章导航界面 前文完成了发送模块的设计&#xff0c;本文接着完成接收模块的设计&#xff0c;接收模块相对发送模块会更加麻烦。 1、设计思路 前文在讲解官方示例工程时&#xff0c;提到GTX IP的接收部分没有做字对齐&#xff0c;需要用户自己编写字对齐…

微服务:Rabbitmq的基本的消息队列的入门简单使用(消息队列中间件)

先介绍最简单的使用方式&#xff0c;后面还会更新其他使用方法。 简单案例 目录结构 引入依赖&#xff1a; <!--AMQP依赖&#xff0c;包含RabbitMQ--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-star…

JAVA:Spring Boot整合Kaptcha验证码实现登录验证

请关注微信公众号&#xff1a;拾荒的小海螺 博客地址&#xff1a;http://lsk-ww.cn/ 1、简述 在Web应用程序中&#xff0c;验证码是一种常见的安全措施&#xff0c;用于验证用户的身份以防止恶意活动&#xff0c;如自动化攻击或机器人。Spring Boot提供了许多库和工具&#x…

UnityAPI学习之Transform组件基本使用

目录 Transform组件 访问与获取 Transform的位置和旋转信息 Transform局部坐标和旋转信息的获取 Transform的缩放与正方向 缩放&#xff08;Scale&#xff09; 正方向 Transform相关的查找方法 销毁游戏物体 Transform组件 访问与获取 现在创建一个容器放置GrisGO物…

VueX核心内容

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介Vuex 核心内容核心概念1. State&#xff08;状态&#xff09;示例&#xff1a; 2. Getter&#xff08;获取器&#xff09;示例&#xff1a; 3. Mutation&#xff08;突变&#xff09;示例&#xff1a; 4. Action&#xff08;动作&a…

MbedTLS源码跨平台编译(window/macos/linux)

1.window平台编译: 克隆: git clone --recursive https://github.com/Mbed-TLS/mbedtls.git 克隆成功 添加OpenSSL环境变量 验证环境 使用cmake编译 cmake ../生成配置时出错 出现上面原因是克隆下来的library与programs及tests目录少文件了,直接下载zip包替换library目录

dibbler-DHCPv6 的开源框架(C++ 实现)1

一、下载 IPv6 DHCPv6 协议的开源框架&#xff1a;dibbler 下载地址&#xff1a;https://github.com/tomaszmrugalski/dibbler.git 二、代码编写语言和文件结构 编写语言 文件 三、编译 编译 server 端&#xff1a; chmod x configure ./configure# 编译服务端(4核) mak…

Renesas MCU之使用e² studio搭建开发环境

目录 概述 1 e studio介绍 2 搭建Renesas MUC开发环境 2.1 软件版本信息 2.2 安装软件 3 创建工程 3.1 板卡硬件接口 3.2 FSP配置IO 4 Generate Project 4.1 项目目录介绍 4.2 LED接口相关驱动 5 调试 5.1 测试代码 5.2 J-Link调试代码 5.3 硬件结构 概述 本文主…

【ARM】

ARM ■ 指令集■ 1. RISC■ 2. CISC ■ ARM简介■ 1.■ 2. ■ ARM-CPU体系架构■ 1. M0■ 2. M3■ 3. M4■ 4. M7■ 5. M7■ 6. M7 ■ ARM-寄存器■ 1. 通用寄存器■ 2.■ 3.■ 4. ■ ARM-工作模式■ ARM-寄存器组■ ARM-异常向量表■ 由于soc0x00000000 是存放IROM芯片出厂数据…

由MapTile引发的ResultSet的思考及实践

其实这篇文章应该是上周末来写的&#xff0c;但是苦逼啊。别人都抱怨工作996&#xff0c;我特么直接9117了&#xff0c;连轴转12天&#xff0c;完全没有个人时间&#xff0c;苦逼啊&#xff01; 本来周末计划看完龙珠Z&#xff08;日语&#xff09;布欧篇 呢&#xff0c;给自己…

喵星人必备!福派斯三文鱼猫粮,营养满分!

猫粮品牌&#xff1a;福派斯三文鱼猫粮测评体验 在快节奏的都市生活中&#xff0c;我们的宠物猫也需要适应当下的生活环境&#xff0c;并保持健康和活力。作为一名合格的铲屎官&#xff0c;我们总是关心如何为猫咪提供既健康又美味的饮食。今天&#xff0c;我有幸为大家带来一…