【51nod 1986 Jason曾不想做的數論題】【數論】

題意

給定n,mn, m,求X[1,m]nlcm(X1,,Xn)gcd(X1,,Xn)\prod_{X\in [1,m]^n}\mathrm{lcm}(X_1,\cdots,X_n)^{\gcd(X_1,\cdots,X_n)}

X[1,m]nX\in [1,m]^n表示XX取遍所有長度爲nn的序列,且XiX_i11mm之間的整數。
n109,m108n\le 10^9,m\le 10^8

分析

先考慮這個問題的弱化版:求

S(m)=X[1,m]nlcm(X1,,Xn)S(m)=\prod_{X\in [1,m]^n}\mathrm{lcm}(X_1,\cdots,X_n)

第一種算法是

S(m)=pP,pkmpmn(mmpk)nS(m)=\prod_{p\in \mathbb{P},p^k\le m}p^{m^n-(m-\lfloor\frac{m}{p^k}\rfloor)^n}

可以預處理

f(n)={pn=pk,pP1otherwisef(n)=\begin{cases} p & n = p^k,p\in \mathbb{P}\\ 1 & otherwise \end{cases}

的前綴積,然後通過分塊可以O(mlogP)O(\sqrt m\log P)求出答案。

第二種算法是通過min-max反演得到

lcm(X1,,Xn)=k=1n1y1<<ykngcd(Xy1,,Xyk)(1)k1\mathrm{lcm}(X_1,\cdots,X_n)=\prod_{k=1}^n\prod_{1\le y_1<\cdots <y_k\le n}\gcd(X_{y_1},\cdots,X_{y_k})^{(-1)^{k-1}}

如果令

g(p)=k=1n1y1<<ykn,1Xyip,1Xim[gcd(Xy1,,Xyk)=1](1)k1g(p)=\sum_{k=1}^n\sum_{1\le y_1<\cdots <y_k\le n,1\le X_{y_i}\le p,1\le X_i\le m}[\gcd(X_{y_1},\cdots,X_{y_k})=1](-1)^{k-1}

那麼

S(m)=k=1mdg(md)S(m)=\prod_{k=1}^md^{g(\lfloor\frac{m}{d}\rfloor)}

注意到gg只有O(m)O(\sqrt m)種取值,只要求出這些gg,就能夠通過分塊O(mlogP)O(\sqrt m\log P)求出答案。問題在於如何求gg。令

gt(p)=k=1n1y1<<ykn,1Xyip,1Xim[gcd(Xy1,,Xyk)=t](1)k1g_t(p)=\sum_{k=1}^n\sum_{1\le y_1<\cdots <y_k\le n,1\le X_{y_i}\le p,1\le X_i\le m}[\gcd(X_{y_1},\cdots,X_{y_k})=t](-1)^{k-1}

A(p)=t=1pgt(p)=k=1n(nk)mnkpk(1)k1A(p)=\sum_{t=1}^pg_t(p)=\sum_{k=1}^n\binom{n}{k}m^{n-k}p^k(-1)^{k-1}

=mnk=0n(nk)mnk(p)k=mn(mp)n=m^n-\sum_{k=0}^n\binom{n}{k}m^{n-k}(-p)^k=m^n-(m-p)^n

由注意到gt(p)=g(pt)g_t(p)=g(\lfloor\frac{p}{t}\rfloor),因此

g(p)=g1(p)=mn(mp)nt=2pg(pt)g(p)=g_1(p)=m^n-(m-p)^n-\sum_{t=2}^pg(\lfloor\frac{p}{t}\rfloor)

用上式計算gg,複雜度和杜教篩類似,都是O(m34)O(m^{\frac{3}{4}})

回到原問題,類似可設

G(p)=X[1,p]nlcm(X1,,Xn)[gcd(X1,,Xn)=1]G(p)=\prod_{X\in [1,p]^n}\mathrm{lcm}(X_1,\cdots,X_n)^{[\gcd(X_1,\cdots,X_n)=1]}

G1(p)=X[1,p]n[gcd(X1,,Xn)=1]G1(p)=\sum_{X\in [1,p]^n}[\gcd(X_1,\cdots,X_n)=1]

那麼

ans=d=1mG(md)d(dd)G1(md)ans=\prod_{d=1}^m G(\lfloor\frac{m}{d}\rfloor)^d*(d^d)^{G1(\lfloor\frac{m}{d}\rfloor)}

如果求出了GGG1G1以及ddd^d的前綴積,就可以分塊來求ansans了。求ddd^d的前綴積可以通過

i=1nii=i=1n(i!(i1)!)i=(n!)ni=1n1i!\prod_{i=1}^ni^i=\prod_{i=1}^n\Big(\frac{i!}{(i-1)!}\Big)^i=\frac{(n!)^n}{\prod_{i=1}^{n-1}i!}

因此只需要O(m)O(m)預處理階乘的前綴積。

用求gg相同的方式求求GGG1G1。令

G1t(p)=X[1,p]n[gcd(X1,,Xn)=t]G1_t(p)=\sum_{X\in [1,p]^n}[\gcd(X_1,\cdots,X_n)=t]

那麼

t=1pG1t(p)=pnG1(p)=pnt=2pG1(pt)\sum_{t=1}^pG1_t(p)=p^n\Rightarrow G1(p)=p^n-\sum_{t=2}^pG1(\lfloor\frac{p}{t}\rfloor)

Gt(p)=X[1,p]nlcm(X1,,Xn)gcd(X1,,Xn)=tG_t(p)=\prod_{X\in [1,p]^n}\mathrm{lcm}(X_1,\cdots,X_n)^{\gcd(X_1,\cdots,X_n)=t}

那麼

t=1pGt(p)=X[1,p]nlcm(X1,,Xn)=S(p)\prod_{t=1}^pG_t(p)=\prod_{X\in [1,p]^n}\mathrm{lcm}(X_1,\cdots,X_n)=S(p)

得到

G(p)=S(p)(t=2pG(pt)tG1(pt))1G(p)=S(p)*\Big(\prod_{t=2}^pG(\lfloor\frac{p}{t}\rfloor)*t^{G1(\lfloor\frac{p}{t}\rfloor)}\Big)^{-1}

在求S(p)S(p)的時候,可以當pp較小時用第一種算法,pp較大時用第二種算法。

代碼

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

const int MOD = 1000000007;
const int MOD1 = MOD - 1;
const int N = 1e8;
const int M = 5e6;
const int K = 2e4 + 5;

int n, m, jc1[N + 5], jc2[N + 5], po[M + 5], tot, prime[M / 10], inv[M + 5], pro[M + 5], pro_inv[M + 5];
int w[K], w1[K], g[K], G[K], G1[K], sqrtm, h1[K], h2[K];
bool not_prime[M + 5];

int ksm(int x, int y, int mo)
{
	int ans = 1;
	while (y)
	{
		if (y & 1) ans = (LL)ans * x % mo;
		x = (LL)x * x % mo; y >>= 1;
	}
	return ans;
}

void pre()
{
	po[1] = 1;
	for (int i = 2; i <= M; i++)
	{
		if (!not_prime[i]) po[i] = ksm(i, n, MOD1);
		for (int j = 1; j <= tot && i * prime[j] <= M; j++)
		{
			po[i * prime[j]] = (LL)po[i] * po[prime[j]] % MOD1;
			if (i % prime[j] == 0) break;
		}
	}
}

void init()
{
	jc1[0] = jc2[0] = inv[0] = inv[1] = 1;
	for (int i = 1; i <= N; i++) jc1[i] = (LL)jc1[i - 1] * i % MOD, jc2[i] = (LL)jc2[i - 1] * jc1[i] % MOD;;
	for (int i = 2; i <= M; i++) inv[i] = (LL)(MOD - MOD / i) * inv[MOD % i] % MOD;
	for (int i = 0; i <= M; i++) pro[i] = pro_inv[i] = 1;
	for (int i = 2; i <= M; i++)
	{
		if (!not_prime[i])
		{
			prime[++tot] = i;
			for (int j = 1; j <= M / i; j *= i) pro[i * j] = i, pro_inv[i * j] = inv[i];
		}
		pro[i] = (LL)pro[i - 1] * pro[i] % MOD;
		pro_inv[i] = (LL)pro_inv[i - 1] * pro_inv[i] % MOD;
		for (int j = 1; j <= tot && i * prime[j] <= M; j++)
		{
			not_prime[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

void pre_calc_g(int m)
{
	int num = 0;
	for (int i = 1; i <= m; i = m / (m / i) + 1) w1[++num] = m / i;
	int t = ksm(m, n, MOD1);
	for (int i = num; i >= 1; i--)
	{
		int p = w1[i], id = p <= sqrtm ? h1[p] : h2[m / p];
		g[id] = (t + MOD1 - ksm(m - p, n, MOD1)) % MOD1;
		for (int j = 2, last; j <= p; j = last + 1)
		{
			last = p / (p / j);
			int q = p / j, id1 = q <= sqrtm ? h1[q] : h2[m / q];
			(g[id] += MOD1 - (LL)(last - j + 1) * g[id1] % MOD1) %= MOD1;
		}
	}
}

int get_S(int m)
{
	if (m <= M)
	{
		int ans = ksm(pro[m], po[m], MOD);
		for (int i = 1, last; i <= m; i = last + 1)
		{
			last = m / (m / i);
			ans = (LL)ans * ksm((LL)pro_inv[last] * pro[i - 1] % MOD, po[m - m / i], MOD) % MOD;
		}
		return ans;
	}
	pre_calc_g(m);
	int ans = 1;
	for (int i = 1, last; i <= m; i = last + 1)
	{
		last = m / (m / i);
		int p = m / i, id = p <= sqrtm ? h1[p] : h2[m / p];
		ans = (LL)ans * ksm((LL)jc1[last] * ksm(jc1[i - 1], MOD - 2, MOD) % MOD, g[id], MOD) % MOD;
	}
	return ans;
}

void pre_calc_G(int num)
{
	for (int i = num; i >= 1; i--)
	{
		int p = w[i], id = p <= sqrtm ? h1[p] : h2[m / p];
		G1[id] = ksm(p, n, MOD1); G[id] = 1;
		for (int j = 2, last; j <= p; j = last + 1)
		{
			last = p / (p / j);
			int q = p / j, id1 = q <= sqrtm ? h1[q] : h2[m / q];
			(G1[id] += MOD1 - (LL)(last - j + 1) * G1[id1] % MOD1) %= MOD1;
			G[id] = (LL)G[id] * ksm(G[id1], last - j + 1, MOD) % MOD;
			G[id] = (LL)G[id] * ksm((LL)jc1[last] * ksm(jc1[j - 1], MOD - 2, MOD) % MOD, G1[id1], MOD) % MOD;
		}
		G[id] = (LL)get_S(p) * ksm(G[id], MOD - 2, MOD) % MOD;
	}
}

int get_pro(int l, int r)
{
	int ans = (LL)ksm(jc1[r], r, MOD) * ksm(jc2[r - 1], MOD - 2, MOD) % MOD;
	if (l <= 1) return ans;
	int w = (LL)ksm(jc1[l - 1], l - 1, MOD) * ksm(jc2[l - 2], MOD - 2, MOD) % MOD;
	return (LL)ans * ksm(w, MOD - 2, MOD) % MOD;
}

int solve()
{
	pre();
	int num = 0; sqrtm = sqrt(m);
	for (int i = 1, last; i <= m; i = last + 1)
	{
		last = m / (m / i); w[++num] = m / i;
		if (m / i <= sqrtm) h1[m / i] = num;
		else h2[last] = num;
	}
	pre_calc_G(num);
	int ans = 1;
	for (int i = 1, last; i <= m; i = last + 1)
	{
		last = m / (m / i);
		int p = m / i, id = p <= sqrtm ? h1[p] : h2[m / p];
		ans = (LL)ans * ksm(G[id], (LL)(last - i + 1) * (last + i) / 2 % MOD1, MOD) % MOD;
		ans = (LL)ans * ksm(get_pro(i, last), G1[id], MOD) % MOD;
	}
	return ans;
}

int main()
{
	int T; scanf("%d", &T);
	init();
	while (T--)
	{
		scanf("%d%d", &n, &m);
		printf("%d\n", solve());
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章