luogu3704 [SDOI2017]数字表格(莫比乌斯反演)

link

\(f_0=0,f_1=1,f_n=f_{n-1}+f_{n-2}(n\ge 2)\)

\(\prod_{i=1}^n\prod_{j=1}^mf_{\gcd(i,j)}\),多组询问,\(T\le1000,n,m\le10^6\)

推导过程稍微有点难,因为有prod而不是清一色的sum了 不过总体还是不难的

\(\prod_{i=1}^n\prod_{j=1}^mf_{\gcd(i,j)}\)

\(=\prod_{p=1}^nf_p^{\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=p]}\)

\(=\prod_{p=1}^nf_p^{\sum_{i=1}^{n/p}\sum_{j=1}^{m/p}[\gcd(i,j)=1]}\)

\(=\prod_{p=1}^nf_p^{\sum_{i=1}^{n/p}\sum_{j=1}^{m/p}\sum_{d|i,d|j}\mu(d)}\)

\(=\prod_{p=1}^nf_p^{\sum_{d=1}^n\mu(d)\lfloor\frac n{pd}\rfloor\lfloor\frac m{pd}\rfloor}\)

\(=\prod_{p=1}^nf_p^{\sum_{d=1}^n\mu(d)\lfloor\frac n{pd}\rfloor\lfloor\frac m{pd}\rfloor}\)

\(=\prod_{q=1}^n\left(\prod_{d|q}f_{q/d}^{\mu(d)}\right)^{\lfloor\frac n{q}\rfloor\lfloor\frac m{q}\rfloor}\)

(就最后一步好像难一点

线性筛\(\mu\)

\(\prod_{d|q}f_{q/d}^{\mu(d)}\)部分可以nlogn枚举倍数预处理

然后做个前缀乘积,后面直接打个逆元就行了

\(O(t\sqrt n\log n)\)

#include <cstdio>
#include <iostream>
using namespace std;

const int p = 1000000007;
int f[1000010], invf[1000010], fuck = 1000000, mu[1000010], prime[1000000], tot;
int s[1000010];
bool vis[1000010];

int qpow(int x, long long y)
{
	int res = 1;
	while (y > 0)
	{
		if (y & 1) res = res * (long long)x % p;
		x = x * (long long)x % p;
		y >>= 1;
	}
	return res;
}

int main()
{
	mu[1] = 1;
	for (int i = 2; i <= fuck; i++)
	{
		if (vis[i] == false) prime[++tot] = i, mu[i] = -1;
		for (int j = 1; j <= tot && i * prime[j] <= fuck; j++)
		{
			vis[i * prime[j]] = true;
			if (i % prime[j] == 0) break;
			mu[i * prime[j]] = -mu[i];
		}
	}
	f[1] = invf[1] = s[1] = 1;
	for (int i = 2; i <= fuck; i++) f[i] = (f[i - 1] + f[i - 2]) % p, invf[i] = qpow(f[i], p - 2), s[i] = 1;
	for (int d = 1; d <= fuck; d++)
	{
		if (mu[d] == -1)
			for (int q = d, cnt = 1; q <= fuck; q += d, cnt++)
				s[q] = s[q] * (long long)invf[cnt] % p;
		if (mu[d] == 1)
			for (int q = d, cnt = 1; q <= fuck; q += d, cnt++)
				s[q] = s[q] * (long long)f[cnt] % p;
	}
	s[0] = 1;
	for (int i = 2; i <= fuck; i++) s[i] = s[i] * (long long)s[i - 1] % p;
	int t;
	scanf("%d", &t);
	while (t --> 0)
	{
		int ans = 1;
		int n, m; scanf("%d%d", &n, &m); if (n > m) { int t = n; n = m; m = t; }
		for (int i = 1, j; i <= n; i = j + 1)
		{
			j = min(n / (n / i), m / (m / i));
			ans = ans * (long long)qpow(s[j] * (long long)qpow(s[i - 1], p - 2) % p, (n / i) * (long long)(m / i)) % p;
		}
		printf("%d\n", ans);
	}
	return 0;
}

这次终于不用define int long long可

posted @ 2019-01-21 10:12  ghj1222  阅读(153)  评论(0编辑  收藏  举报