洛谷 P3704 :[SDOI2017]数字表格(莫比乌斯反演)

在这里插入图片描述


题目让你求 ∏ i = 1 n ∏ j = 1 m f [ g c d ( i , j ) ] \prod_{i = 1}^n\prod_{j = 1}^m f[gcd(i,j)] i=1nj=1mf[gcd(i,j)]
化简一下式子,枚举一下 gcd: ∏ d = 1 n ∏ i = 1 n ∏ j = 1 m f [ d ] ∗ [ g c d ( i , j ) = d ] = ∏ d = 1 n f ( d ) ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] \prod_{d = 1}^n\prod_{i = 1}^n\prod_{j = 1}^m f[d] * [gcd(i,j)=d] = \prod_{d = 1}^nf(d)^{\sum_{i = 1}^n\sum_{j = 1}^m[gcd(i,j) = d]} d=1ni=1nj=1mf[d][gcd(i,j)=d]=d=1nf(d)i=1nj=1m[gcd(i,j)=d]观察这个式子, f ( d ) f(d) f(d)的指数部分可以拿出来反演化简了: ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] = ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ] \sum_{i = 1}^n\sum_{j = 1}^m[gcd(i,j) = d] = \sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j = 1}^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j) = 1] i=1nj=1m[gcd(i,j)=d]=i=1dnj=1dm[gcd(i,j)=1]
令 f ( p ) = ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = p ] 令f(p) = \sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j = 1}^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j) = p] f(p)=i=1dnj=1dm[gcd(i,j)=p]
F ( x ) = ∑ x ∣ p f [ p ] = ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ x ∣ g c d ( i , j ) ] F(x) = \sum_{x | p}f[p] = \sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j = 1}^{\lfloor\frac{m}{d}\rfloor}[x|gcd(i,j)] F(x)=xpf[p]=i=1dnj=1dm[xgcd(i,j)]
反 演 一 下 : f ( p ) = ∑ p ∣ x μ ( x p ) ∗ F ( x ) , f ( 1 ) = ∑ x = 1 ⌊ n d ⌋ μ ( x ) ∗ F ( x ) = ∑ x = 1 ⌊ n d ⌋ μ ( x ) ∗ ⌊ n x ∗ d ⌋ ∗ ⌊ m x ∗ d ⌋ 反演一下:f(p) = \sum_{p | x}\mu(\frac{x}{p}) *F(x),f(1) = \sum_{x = 1}^{\lfloor\frac{n}{d}\rfloor}\mu(x) *F(x) = \sum_{x = 1}^{\lfloor\frac{n}{d}\rfloor}\mu(x)*{\lfloor\frac{n}{x * d}\rfloor}*{\lfloor\frac{m}{x * d}\rfloor} f(p)=pxμ(px)F(x)f(1)=x=1dnμ(x)F(x)=x=1dnμ(x)xdnxdm
化简到这个程度可以 O ( n n ) O(n\sqrt n) O(nn )预处理指数部分,然后每次询问分块,用欧拉降幂加快速幂在 O ( T ∗ n ∗ l o g ( m o d ) ) O(T * \sqrt n * log(mod)) O(Tn log(mod))时间内完成单组询问,但 n ≤ 1 0 6 , O ( n ∗ n ) n \leq 10 ^6 ,O(n * \sqrt n) n106O(nn )不够通过此题,继续化简,将指数部分枚举 x 的地方拿下来: ∏ d = 1 n f ( d ) ∑ x = 1 ⌊ n d ⌋ μ ( x ) ∗ ⌊ n x ∗ d ⌋ ∗ ⌊ m x ∗ d ⌋ = ∏ d = 1 n ∏ x = 1 ⌊ n d ⌋ f ( d ) m u ( x ) ∗ ⌊ n x ∗ d ⌋ ∗ ⌊ m x ∗ d ⌋ \prod_{d = 1}^nf(d)^{\sum_{x = 1}^{\lfloor\frac{n}{d}\rfloor}\mu(x)*{\lfloor\frac{n}{x * d}\rfloor}*{\lfloor\frac{m}{x * d}\rfloor}} = \prod_{d = 1}^n\prod_{x = 1}^{\lfloor\frac{n}{d}\rfloor}f(d)^{\\mu(x)*{\lfloor\frac{n}{x * d}\rfloor}*{\lfloor\frac{m}{x * d}\rfloor}} d=1nf(d)x=1dnμ(x)xdnxdm=d=1nx=1dnf(d)mu(x)xdnxdm
T = x ∗ d T = x * d T=xd,改变枚举项: ∏ T = 1 n ∏ d ∣ T f ( d ) m u ( T d ) ∗ ⌊ n T ⌋ ∗ ⌊ m T ⌋ \prod_{T = 1}^n\prod_{d | T}f(d)^{\\mu(\frac{T}{d})*{\lfloor\frac{n}{T}\rfloor}*{\lfloor\frac{m}{T}\rfloor}} T=1ndTf(d)mu(dT)TnTm
S ( T ) = ∏ d ∣ T f ( d ) μ ( T d ) S(T) = \prod_{d | T}f(d)^{\mu(\frac{T}{d})} S(T)=dTf(d)μ(dT),可以 O ( n ∗ l o g n ) O(n * logn) O(nlogn)预处理得到 S函数。
∏ T = 1 n S ( T ) ⌊ n T ⌋ ∗ ⌊ m T ⌋ \prod_{T = 1}^nS(T)^{{\lfloor\frac{n}{T}\rfloor}*{\lfloor\frac{m}{T}\rfloor}} T=1nS(T)TnTm
预处理一下S函数前缀乘积和逆元,再分下块,每次询问 O ( n ∗ l o g ( m o d ) ) O(\sqrt n * log(mod)) O(n log(mod))解决,总复杂度 O ( n ∗ l o g n + T ∗ n ∗ l o g ( m o d ) ) O(n * logn + T * \sqrt n * log(mod)) O(nlogn+Tn log(mod))


代码:

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int maxn = 2e6 + 10;
typedef long long ll;
ll fpow(ll a,ll b) {
	ll r = 1;
	while(b) {
		if(b & 1) r = r * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return r;
}
bool ispri[maxn];
int pri[maxn],mu[maxn];
ll ans[maxn],inv[maxn];
int f[maxn];
int t,n,m;
void sieve(int n) {
	ispri[0] = ispri[1] = true;
	pri[0] = 0;mu[1] = 1;
	for(int i = 2; i <= n; i++) {
		if(!ispri[i]) pri[++pri[0]] = i,mu[i] = -1;
		for(int j = 1; j <= pri[0] && i * pri[j] <= n; j++) {
			ispri[i * pri[j]] = true;
			if(i % pri[j] == 0) break;
			mu[i * pri[j]] = -mu[i];
		}
	}
	for(int i = 0; i <= n; i++) ans[i] = 1;
	f[1] = f[2] = 1;
	for(int i = 3; i <= n; i++)
		f[i] = (f[i - 1] + f[i - 2]) % mod;
	for(int i = 1; i <= n; i++)
		inv[i] = fpow(f[i],mod - 2);
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j * i <= n; j++) {
			if(mu[j] == -1)
				ans[i * j] = ans[i * j] * inv[i] % mod;
			else if(mu[j] == 1)
				ans[i * j] = ans[i * j] * f[i] % mod;
		}
	}
	for(int i = 1; i <= n; i++)
		ans[i] = ans[i] * ans[i - 1] % mod;
	for(int i = 0; i <= n; i++)
		inv[i] = fpow(ans[i],mod - 2);		
}
int main() {
	sieve(1000000);
	scanf("%d",&t);
	while(t--) {
		scanf("%d%d",&n,&m);
		int l,r;
		ll res = 1;
		if(n > m) swap(n,m);
		for(l = 1; l <= n; l = r + 1) {
			r = min(n / (n / l),m / (m / l));
			ll p1 = n / l,p2 = m / l;
			ll tmp = ans[r] * inv[l - 1] % mod;
			res = res * fpow(tmp,p1 * p2 % (mod - 1)) % mod;
		}
		printf("%lld\n",res);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值