D. Soldier and Number Game

_Yxc發表於2024-06-22

題意:給出a和b(1 <= b <= a <= 5e6),問a!/b!變成1,最多要經過多少輪?沒輪可以選擇一個它的因子來除它。

思路:質因子數量,先線性篩,再質因子分解每個數,再字首和,然後O1查詢。

總結:在模板中使用範圍質數篩選時,當範圍到了5e6就MLE了,沒法弄,最後用的線性篩+質因子分解。考慮要不要為模板中單獨加一個統計範圍內每個數的質因子數量的函式?

/*
 * PrimeNumberManipulator(質數類)
 * 設計思想:基於物件導向的思想的質數管理類。
 * 基於物件導向的程式設計思想,本方法儘可能多的隱藏了內部實現的細節,並且將必要的程式設計介面暴露在外部,並需要對這些介面進行直接的修改。
 *                             注意事項:第一個模板引數的數值必須是int型(在競賽中範圍最大是1e8)
 *                                      第二個模板引數是代表是否對第一個引數的範圍內每個數進行質數統計(範圍大概是1e6不會TLE)。
 *
 * gitHub(倉庫地址): https://github.com/yxc-s/programming-template.git
 */












template<typename T, typename U>
class PrimeNumberManipulator{
public:
	template<typename V = T>
	using EnableIfInt    =   typename std::enable_if<std::is_same<V, int>::value, V>::type;
	using primeType      =   typename std::decay<decltype(T::value)>::type;
	using rangePrimeType =   typename std::decay<decltype(U::value)>::type;

	PrimeNumberManipulator(EnableIfInt<primeType>* = 0, EnableIfInt<rangePrimeType>* = 0 ){
		sievePrimes();
		getRangePrimes();
	}

	const std::vector<int>& getPrimeArray() const {
		return prime_values_;
	}

	const std::vector<std::vector<std::pair<int, int>>>& getRangePrimesArray() const {
		return range_prime_values_;
	}

	/* 獲取單個數的質因子及出現次數 */
	template <typename V>
	std::vector<std::pair<V, int>> getUniquePrimes(V x){
		std::vector<std::pair<V, int>> res;
		for (const auto& prime : prime_values_){
			if (1ll * prime * prime > x){
				break;
			}
			int cnt = 0;
			while (x % prime == 0){
				x /= prime;
				cnt ++;
			}
			if (cnt){
				res.emplace_back(prime, cnt);
			}
		}
		if (x > 1){
			res.emplace_back(x, 1);
		}
		return res;
	}

	/* 統計不同的質數個數 */
	template<typename V>
	int countUniquePrimes(V x){
		int ans = 0;
		for (const auto& prime : prime_values_){
			if (prime > x / prime){
				break;
			}
			ans ++;
			while (x % prime == 0){
				x /= prime;
			}
		 }
		return ans + (x > 1);
	}

	/* 統計所有的質數 */
	template<typename V>
	int countAllPrimes(V x){
		int ans = 0;
		for (const auto& prime : prime_values_){
			if (prime > x / prime){
				break;
			}
			while (x % prime == 0){
				x /= prime;
				ans ++;
			}
		}
		return ans + (x > 1);
	}

	/* 判定質數,當篩選範圍為1e8時,最多可判斷1e16的質數*/
	template<typename V>
	bool isPrime(V x){
		if (x <= getPrimeLimit()){
			return bs_[x];
		}
		for (const auto& prime : prime_values_){
			if (x % prime == 0){
				return false;
			}
		}
		return true;
	}

	/* 統計因子數量 */
	template<typename V>
	int countDivisors(V x){
		int ans = 1;
		for (const auto& prime : prime_values_){
			if (prime > x / prime){
				break;
			}
			int power = 0;
			while (x % prime == 0){
				x /= prime;
				power ++;
			}
			ans *= power + 1;
		}
		return x > 1 ? ans * 2 : ans;
	}


private:
	std::bitset<100000010>                            bs_;
	std::vector<int>                                  prime_values_;
	std::vector<std::vector<std::pair<int, int>>>     range_prime_values_;


private:
	constexpr primeType      getPrimeLimit()      { return T::value; }
	constexpr rangePrimeType getRangePrimeLimit() { return U::value; }
	/* 線性篩 */
	void sievePrimes(){
		bs_.set();
		bs_[0] = bs_[1] = 0;
		for (int i = 2; i <= getPrimeLimit(); ++i){
			if (bs_[i]){
				prime_values_.emplace_back(i);
			}
			for (const auto& prime : prime_values_){
				if (1ll * i * prime > getPrimeLimit()){
					break;
				}
				bs_[i * prime] = 0;
				if (i % prime == 0){
					break;
				}
			}
		}
	}

	/* 獲取[1, x]範圍內每個數的質因子及其出現次數*/
	void getRangePrimes(){
		range_prime_values_.resize(getRangePrimeLimit() + 1);
		for (int i = 2; i <= getRangePrimeLimit(); ++i){
			if (range_prime_values_[i].empty()){
				for (int j = i; j <= getRangePrimeLimit(); j += i){
					int cnt = 0;
					int num = j; 
					while (num % i == 0){
						cnt ++;
						num /= i;
					}
					range_prime_values_[j].emplace_back(i, cnt);
				}
			}
		}
	}


};


constexpr int N = (int)5e6;
constexpr int  PRIME_VALUE_LIMIT   =   N + 10;              /*最多可以到1e8*/
constexpr int  RANGE_VALUE_LIMIT   =   0;        /*目測最多到1e6*/
using Primer = PrimeNumberManipulator<std::integral_constant<decltype(PRIME_VALUE_LIMIT), PRIME_VALUE_LIMIT>, 
									 std::integral_constant<decltype(PRIME_VALUE_LIMIT), RANGE_VALUE_LIMIT>>;

Primer primer;
const std::vector<int>&                              primes = primer.getPrimeArray();
const std::vector<std::vector<std::pair<int, int>>>& range_primes = primer.getRangePrimesArray();

/*
TODO: 第二個模板引數要不要也換成跟第一個模板引數相同的型別,來單獨進行篩選?
	  將大質數的演算法也寫進去。
	   尤拉函式,莫比烏斯函式。
*/

using namespace std;
array<int, N + 20> pref{};
void preProcess(){
	for (int i = 1; i <= N; ++i){
		pref[i] = pref[i - 1] + primer.countAllPrimes(i);
	}
}




void solve(){
	int a, b;
	cin >> a >> b;
	cout << (pref[a] - pref[b]) << '\n';
}