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

Description
D o r i s Doris Doris刚刚学习了 F i b o n a c c i Fibonacci Fibonacci数列。用 f [ i ] f[i] f[i]表示数列的第i项,那么
f [ 0 ] = 0 , f [ 1 ] = 1 , f [ n ] = f [ n − 1 ] + f [ n − 2 ] , n > = 2 f[0]=0 ,f[1]=1 ,f[n]=f[n-1]+f[n-2],n>=2 f[0]=0,f[1]=1,f[n]=f[n1]+f[n2],n>=2
D o r i s Doris Doris用老师的超级计算机生成了一个 n × m n×m n×m的表格,第 i i i行第 j j j列的格子中的数是 f [ g c d ( i , j ) ] f[gcd(i,j)] f[gcd(i,j)],其中 g c d ( i , j ) gcd(i,j) gcd(i,j)表示 i , j i,j i,j的最大公约数。 D o r i s Doris Doris的表格中共有 n × m n×m n×m个数,她想知道这些数的乘积是多少。答案对 1 0 9 + 7 10^9+7 109+7取模。


Sample Input
3
2 3
4 5
6 7


Sample Output
1
6
960


莫反一下:
∏ d = 1 n f ( d ) ∑ i = 1 n d ∑ j = 1 m d ∑ 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 = 1 n f ( d ) ∑ k = 1 n d μ ( k ) ⌊ n k d ⌋ ⌊ m k d ⌋ \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 = k d T=kd T=kd
∏ T = 1 n f ( d ) ( ∏ d ∣ T f ( d ) μ ( T d ) ) ⌊ n T ⌋ ⌊ m T ⌋ \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 ∗ l o g n ) O(n*logn) O(nlogn)预处理,然后数论分块搞一下时间复杂度就是 O ( T ∗ s q r t n ∗ l o g m o d ) 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值