[Sdoi2017]数字表格 莫比乌斯反演

本文探讨了利用Fibonacci数列生成的矩阵中所有元素乘积的算法,通过数学转换简化计算过程,并使用高效的时间复杂度实现。文章提供了一种新颖的数学视角和算法实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
DorisDorisDoris刚刚学习了FibonacciFibonacciFibonacci数列。用f[i]f[i]f[i]表示数列的第i项,那么
f[0]=0,f[1]=1,f[n]=f[n−1]+f[n−2],n>=2f[0]=0 ,f[1]=1 ,f[n]=f[n-1]+f[n-2],n>=2f[0]=0,f[1]=1,f[n]=f[n1]+f[n2],n>=2
DorisDorisDoris用老师的超级计算机生成了一个n×mn×mn×m的表格,第iii行第jjj列的格子中的数是f[gcd(i,j)]f[gcd(i,j)]f[gcd(i,j)],其中gcd(i,j)gcd(i,j)gcd(i,j)表示i,ji,ji,j的最大公约数。DorisDorisDoris的表格中共有n×mn×mn×m个数,她想知道这些数的乘积是多少。答案对109+710^9+7109+7取模。


Sample Input
3
2 3
4 5
6 7


Sample Output
1
6
960


莫反一下:
∏d=1nf(d)∑i=1nd∑j=1md∑k∣i,k∣jμ(k)\prod_{d=1}^nf(d)^{\sum_{i=1}^{\frac nd}\sum_{j=1}^{\frac md}\sum_{k|i,k|j}\mu(k)}d=1nf(d)i=1dnj=1dmki,kjμ(k)
=>=>=>
∏d=1nf(d)∑k=1ndμ(k)⌊nkd⌋⌊mkd⌋\prod_{d=1}^nf(d)^{\sum_{k=1}^{\frac nd}\mu(k)\lfloor \frac n{kd}\rfloor\lfloor \frac m{kd}\rfloor}d=1nf(d)k=1dnμ(k)kdnkdm
然后用一个老套路,设T=kdT=kdT=kd
∏T=1nf(d)(∏d∣Tf(d)μ(Td))⌊nT⌋⌊mT⌋\prod_{T=1}^n f(d)(\prod_{d|T}f(d)^{\mu(\frac Td)})^{\lfloor \frac nT\rfloor\lfloor \frac mT\rfloor}T=1nf(d)(dTf(d)μ(dT))TnTm
中间的式子可以O(n∗logn)O(n*logn)O(nlogn)预处理,然后数论分块搞一下时间复杂度就是O(T∗sqrtn∗logmod)O(T*sqrtn*logmod)O(Tsqrtnlogmod)


#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const int mod = 1e9 + 7;
const int N = 1000001;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

bool v[N];
int plen, mu[N], p[N], f[N], inv[N];
LL s[N];

int add(int x, int y) {
	x += y;
	return x >= mod ? x - mod : x;
}

LL pow_mod(LL a, LL k) {
	LL ans = 1;
	while(k) {
		if(k & 1) (ans *= a) %= mod;
		(a *= a) %= mod; k /= 2;
	} return ans;
}

void get_p() {
	mu[1] = s[1] = s[0] = 1;
	for(int i = 2; i < N; i++) {
		if(!v[i]) p[++plen] = i, mu[i] = -1;
		for(int j = 1; j <= plen && (LL)i * p[j] < N; j++) {
			v[i * p[j]] = 1;
			if(i % p[j] == 0) {mu[i * p[j]] = 0; break;}
			else mu[i * p[j]] = -mu[i];
		} s[i] = 1;
	}
}

int main() {
	get_p();
	f[0] = 0, f[1] = 1, inv[1] = 1;
	for(int i = 2; i < N; i++) f[i] = add(f[i - 1], f[i - 2]), inv[i] = pow_mod(f[i], mod - 2);
	for(int i = 1; i < N; i++) {
		for(int j = i, o = 1; j < N; j += i, ++o) {
			(s[j] *= mu[o] == -1 ? inv[i] : pow_mod(f[i], mu[o])) %= mod;
		}
	} for(int i = 1; i < N; i++) (s[i] *= s[i - 1]) %= mod;
	int tt = read();
	while(tt--) {
		int n = read(), m = read();
		if(n > m) swap(n, m); LL ans = 1;
		for(int l = 1, r; l <= n; l = r + 1) {
			r = _min(n / (n / l), m / (m / l));
			LL hh = s[r] * pow_mod(s[l - 1], mod - 2) % mod;
			(ans *= pow_mod(hh, (LL)(n / l) * (m / l) % (mod - 1))) %= mod;
		} put(ans), puts("");
	} return 0;
}

资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 最近在使用 MongoDB 3.0.6 版本时,小编遇到了一个棘手的问题:在对集合执行大规模排序操作(如聚合)时,出现了错误提示。今天就来分享一下如何快速解决 MongoDB 排序操作超出内存限制的问题。 MongoDB 是一款广受欢迎的开源文档型数据库,凭借其出色的性能、高可用性和可扩展性而备受青睐。但在处理海量数据集时,尤其是涉及排序操作时,很容易碰到内存限制的瓶颈。MongoDB 在执行排序操作时,默认会使用内存来完成,以保证操作的高效性。不过,为了防止过度占用系统资源,MongoDB 对内存中的排序操作设置了上限,通常为 100MB(在 3.0.6 版本中)。一旦排序的数据量超出了这个限制,就会出现类似以下的错误: 该错误表明,排序操作超出了 100MB 的内存限制,且未启用外部排序功能。为了解决这一问题,可以使用allowDiskUse选。allowDiskUse允许 MongoDB 在排序时借助磁盘空间,而不再仅依赖内存。具体操作是在聚合查询或排序操作中加入{allowDiskUse: true}。例如,针对上述错误,可以将查询语句修改为: 启用allowDiskUse后,MongoDB 会将排序数据写入临时文件,并在磁盘上完成排序。虽然这种方式可能会因磁盘 I/O 的延迟而降低排序速度,但它能够有效处理大规模数据集。 不过,需要注意的是,虽然allowDiskUse可以解决内存限制问题,但其对性能的影响也不容忽视。在处理大量数据时,建议优化查询语句,减少需要排序的文档数量,或者考虑采用其他数据存储和查询策略,比如分片(sharding)或预计算索引等。此外,保持数据库版本的更新也非常重要。MongoDB 的后续版本可能在内存管理和排序机制方面进行了优化,例如提升了内存限
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值