1. 程式人生 > 其它 >「CQOI2015」選數

「CQOI2015」選數

題目描述

我們知道,從區間 \([L,H]\)\(L\)\(H\) 為整數)中選取 \(N\) 個整數,總共有 \((H-L+1)^N\) 種方案。小 \(z\) 很好奇這樣選出的數的最大公約數的規律,他決定對每種方案選出的 \(N\) 個整數都求一次最大公約數,以便進一步研究。然而他很快發現工作量太大了,於是向你尋求幫助。你的任務很簡單,小 \(z\) 會告訴你一個整數 \(K\),你需要回答他最大公約數剛好為 \(K\) 的選取方案有多少個。

由於方案數較大,你只需要輸出其除以 \(10^9+7\) 的餘數即可。

輸入格式

輸入一行,包含四個空格分開的正整數,依次為 \(N,K,L,H\)

輸出格式

輸出一個整數,為所求方案數除以 \(10^9+7\) 的餘數。

輸入輸出樣例

輸入

2 2 2 4

輸出

3

說明/提示

樣例解釋

所有可能的選擇方案:\((2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)\)

其中最大公約數等於 \(2\) 的只有 \(3\) 組:\((2, 2), (2, 4), (4, 2)\)

資料規模與約定

對於 \(100\%\) 的資料,\(1\le N,K\le 10^9\)\(1\le L\le H\le 10^9\)\(H-L\le 10^5\)

題解

問題可以直接轉化為一個式子,然後進行化簡

\[\begin{aligned} \sum_{i=L}^H\sum_{j=L}^H[\gcd(i,j)=k] &=\sum_{i=\lfloor \frac{L}{k} \rfloor}^{\lfloor \frac{H}{k} \rfloor}\sum_{j=\lfloor \frac{L}{k} \rfloor}^{\lfloor \frac{H}{k} \rfloor}[\gcd(i,j)=1]\\ &=\sum_{i=\lfloor \frac{L}{k} \rfloor}^{\lfloor \frac{H}{k} \rfloor}\sum_{j=\lfloor \frac{L}{k} \rfloor}^{\lfloor \frac{H}{k} \rfloor}\sum_{d|i,d|j}\mu(d)\\ &=\sum_{i=\lfloor \frac{L}{k} \rfloor}^{\lfloor \frac{H}{k} \rfloor}\sum_{j=\lfloor \frac{L}{k}\rfloor}^{\lfloor \frac{H}{k} \rfloor}\sum_{d|i,d|j}\mu(d)\\ &=\sum_{d=1}^{\lfloor \frac{H}{k} \rfloor}\mu(d) \sum_{i=\lfloor \frac{L}{k} \rfloor}^{\lfloor \frac{H}{k} \rfloor}\sum_{j=\lfloor \frac{L}{k}\rfloor}^{\lfloor \frac{H}{k} \rfloor}[d\mid i][d\mid j]\\ &=\sum_{d=1}^{\lfloor \frac{H}{k} \rfloor}\mu(d) (\left \lfloor \frac{\lfloor \frac{H}{k} \rfloor}{d} \right \rfloor - \left \lfloor \frac{\lfloor \frac{L-1}{k} \rfloor}{d} \right \rfloor)^2 \end{aligned} \]

前面的 \(\mu\)

杜教篩掉就行了,外層的迴圈整除分塊去做,總複雜度是 \(\mathcal{O}(n^{\frac{2}{3}})\)

程式碼
#include <cstdio>
#include <iostream>
#include <unordered_map>

typedef long long ll;

using namespace std;

const int mod = 1e9 + 7;

inline int addmod (register int a, register int b) {
	return a += b, a >= mod ? a - mod : a;
}

inline int delmod (register int a, register int b) {
	return a -= b, a < 0 ? a + mod : a;
}

inline ll mulmod (register ll a, register int b) {
	return a *= b, a >= mod ? a % mod : a;
}

inline int qpow (register int a, register int b, register int ans = 1) {
	for (; b; b >>= 1, a = mulmod (a, a))
		if (b & 1) ans = mulmod (ans, a);
	return ans;
}

int n, m, L, R, cnt;
int prime[3000005], mul[3000005], sum[3000005];
bool vis[3000005];
unordered_map <int, int> f;
	
inline void xxs () {
	mul[1] = sum[1] = 1;
	for (register int i = 2; i <= 3e6; i ++) {
		if (! vis[i]) prime[++ cnt] = i, mul[i] = mod - 1;
		for (register int j = 1; j <= cnt && i * prime[j] <= 3e6; j ++) {
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
			mul[i * prime[j]] = mod - mul[i];
		}
		sum[i] = addmod (sum[i - 1], mul[i]);
	}
}

inline int F (register int n, register int ans = 1) {
	if (n <= 3e6) return sum[n];
	if (f[n]) return f[n];
	for (register int l = 2, r; l <= n; l = r + 1) 
		r = n / (n / l), ans = delmod (ans, mulmod (r - l + 1, F (n / l)));
	return f[n] = ans;
}

inline int Calc (register int n, register int m, register int k, register int ans = 0) {
	for (register int l = 1, r; l <= m; l = r + 1) 
		r = n < l ? m / (m / l) : min (n / (n / l), m / (m / l)), 
		ans = addmod (ans, mulmod (delmod (F (r), F (l - 1)), qpow (delmod (m / l, n / l), k)));
	return ans;
}

int main () {
	scanf ("%d%d%d%d", &n, &m, &L, &R), L = (L - 1) / m, R = R / m, xxs ();
	return printf ("%d\n", Calc (L, R, n)), 0;
}