Loading

2020 China Collegiate Programming Contest, Weihai Site L. Clock Master(分组背包/数论)

With the rapid development of society, the demand for high-precision clocks is constantly rising. Recently, the China Clock Production Company is developing a new type of clock, which can represent a wide range of times.

The novel clock displays the current time in an unusual fashion. The clock consists of several pointers, each controlled by a gear. All gears rotate synchronously – one tooth per period. However, the numbers of teeth of the gears may differ. If a gear has 𝑡t teeth, then the corresponding pointer can point to 𝑡t different directions, denoted 0,1,2,⋯,𝑡−10,1,2,⋯,t−1, respectively, where 00 is the initial direction. Furthermore, if a clock is equipped with 𝑛n pointers, the 𝑖i-th of which is controlled by a 𝑡𝑖ti-tooth gear, then the 𝑖i-th pointer will point to 𝑘mod𝑡𝑖kmodti after 𝑘k periods of time.

The price for a 𝑡t-tooth gear is 𝑡t yuan. Given a total budget of 𝑏b yuan, you need to design a combination of gears, such that the number of valid combinations of directions of pointers is maximized, and the total cost on gears does not exceed the budget. A combination of directions (𝑑1,𝑑2,⋯,𝑑𝑛)(d1,d2,⋯,dn) is valid, if it can be written

(𝑘mod𝑡1,𝑘mod𝑡2,⋯,𝑘mod𝑡𝑛)(kmodt1,kmodt2,⋯,kmodtn)

for some nonnegative integer 𝑘k, where 𝑡𝑖ti is the number of teeth of the 𝑖i-th gear. Since the answer may be too large, output the answer in natural logarithm (logarithm with base 𝑒=2.718281828⋯)e=2.718281828⋯).

Input

The first line of input is a single integer 𝑇T (1≤𝑇≤30000)(1≤T≤30000), indicating the number of test cases. Each test case is a single line of an integer 𝑏b (1≤𝑏≤30000)(1≤b≤30000), denoting the total budget.

Output

For each test case, print the natural logarithm, within an absolute or relative error of no more than 10−610−6, of the maximum number of valid combinations, in a single line.

Example

input

Copy

3
2
7
10

output

Copy

0.693147181
2.484906650
3.401197382

题意看了好半天没看明白,其实就是给定一个数b,找到若干个数,使得这若干个数的最小公倍数最大同时保证这些数的和小于等于b。输出对这个最大的最小公倍数取ln的结果。

因为想要让最小公倍数最大,肯定希望这若干个数尽可能两两互质。考虑如何选取。由唯一分解定理可知,一个数总能写成若干个质数的次幂的乘积,两个数互质当且仅当其唯一分解后的公共质因子的幂次为0。同时,假设我们选了两个数\(x_1, x_2\),其唯一分解后得到\(x_1 = p_1^{c_1}p_2^{c_2},x_2=p_3^{c_3}p_4^{c_4}\),则\(lcm(x_1, x_2) = x_1\times x_2\),但如果我们选择\(p_1^{c_1},p_2^{c_2},p_3^{c_3},p_4^{c_4}\),其lcm也为\(x_1\times x_2\),且这样选择更优,因为当a, b不为1时\(a + b < a \times b\),所以分开后费用更小。所以考虑选择质数的次幂。因为\(p^{k1} | p ^ {k2}(k_1 < k_2)\),所以实际上每个素数只选择一个次幂,这就抽象成了一个分组背包的模型。设dp[i, j]表示从前i组素数次幂中选取和为j的数能得到的最大的lcm取ln得到的答案。由于要求输出的是取ln的答案,因此预处理出ln[x]数组后就能很方便地转移了。预处理出dp数组,询问时直接查询输出即可。

#include <bits/stdc++.h>
using namespace std;
double dp[305][30005];//dp[i][j]表示从前i组素数次幂中选取和为j的数能得到的最大的lcm取ln得到的答案
const double E = 2.718281828459;
double ln[30005];
int p[30005], m = 0;
bool vis[30005] = { 0 };
void primes(int n) {
	for(int i = 2; i <= n; i++) {
		if(vis[i]) continue;
		m++;
		p[m] = i;
		for(int j = 1; i * j <= n; j++) vis[i * j] = 1;
	}
}
int main() {
	int t;
	cin >> t;
	primes(30000);
	memset(dp, 0, sizeof(dp));
	for(int i = 1; i <= 30000; i++) {

		ln[i] = log(i) * 1.0 / log(E);
	}
	for(int i = 1; i <= 303; i++) {
		for(int j = 1; j <= 30000; j++) {
			int np = p[i];
			for(int k = 1; np <= 30000; k++) {//np是p[i]^k
				dp[i][j] = max(dp[i][j], dp[i - 1][j]);
				if(j >= np) dp[i][j] = max(dp[i][j], dp[i - 1][j - np] + ln[np]);
				np *= p[i];
			}
		}
	}
	while(t--) {
		long long b;
		scanf("%lld", &b);
		printf("%.9lf\n", dp[300][b]);
	}
	return 0;
}
posted @ 2021-05-12 17:02  脂环  阅读(107)  评论(0编辑  收藏  举报