A

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

int gcd(int a, int b) {
   
	return b ? gcd(b, a % b) : a;
}

int main() {
   
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	int a, b;
	cin >> a >> b;
	int res = gcd(a, b);
	if (res == 1) cout << "YES" << "\n";
	else cout << "NO" << "\n";

	return 0;
}

B

朴素做法时间复杂度O(n2log⁡n)O(n ^ 2\log{}{n})O(n2logn), 会超时

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

int n;
int w[N], f[N];

int gcd(int a, int b) {
   
	return b ? gcd(b, a % b) : a;
}

bool check(int a, int b) {
   
	return gcd(a, b) == 1;
}

int main() {
   
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> w[i];

	int res = 1;
	for (int i = 1; i <= n; ++i) {
   
		f[i] = 1;
		for (int j = 1; j < i; ++j) {
   
			if (!check(w[j], w[i])) f[i] = max(f[i], f[j] + 1);
		}
		res = max(res, f[i]);
	}

	cout << res << "\n";
	return 0;
}

需要优化, 如何找到aia_iai前面与aia_iai不互质的数的位置, aia_iai质因数的个数是log⁡ai\log{}{a_i}logai, 与aia_iai不互质的数的位置一定至少与aia_iai有一个公共的质因子

原来是暴力枚举所有非互质数, 现在可将整个集合进行划分, 划分成与aia_iai有相同的质因子是p1p_1p1的数, 是p2p_2p2的数…

g[i]g[i]g[i]代表所有包含质因子iiif[j]f[j]f[j]的最大值, 这样处理后状态转移方程转化为
f[i]=max⁡(g[2],g[3],...,g[k])+1f[i] = \max(g[2], g[3], ..., g[k]) + 1f[i]=max(g[2],g[3],...,g[k])+1, 求f[i]f[i]f[i]后更新ggg

时间的复杂度O(nn)O(n\sqrt n)O(nn ), 时间瓶颈在试除法分解质因数, 或者使用筛法优化, 时间复杂度O(nlog⁡n)O(n\log n)O(nlogn)

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

int n;
int w[N], f[N], primes[N], g[N];

int main() {
   
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> w[i];
	
	// 筛法求每个数的最小质因子
	for (int i = 2; i < N; ++i) {
   
		for (int j = i; j < N; j += i) {
   
			if (!primes[j]) primes[j] = i;
		}
	}

	int res = 0;
	for (int i = 1; i <= n; ++i) {
   
		f[i] = 1;
		
		// 枚举每个数的质因子
		for (int j = w[i]; j > 1; j /= primes[j]) {
   
			f[i] = max(f[i], g[primes[j]] + 1);
		}   
		
		// 更新每个质因子对应的最大的f的值
		for (int j = w[i]; j > 1; j /= primes[j]) {
   
			g[primes[j]] = max(g[primes[j]], f[i]);
		}

		res = max(res, f[i]);
	}

	cout << res << "\n";

	return 0;
}

注意j≠0j \ne 0j=0, 因为000没有最小质因子

C

将问题集合进行分类, 将集合按照第111个不能上车的奶牛进行划分, 如果所有奶牛都能上车进行特判

在这里插入图片描述
将集合继续进行划分, 前面有jjj头牛, 后面有n−j−1n - j - 1nj1头牛, 在对重量进行划分
f[i][j]f[i][j]f[i][j]表示选择了jjj头牛并且总重量是kkk的方案数, 发生的概率就是f[i][j]n!\frac{f[i][j]}{n!}n!f[i][j], 因为前后奶牛的顺序可以任意调整, 因此概率为

f[i][j]×j!×(n−j−1)!n! \frac{f[i][j] \times j! \times (n - j - 1)!}{n!} n!f[i][j]×j!×(nj1)!

如何计算f[j][k]f[j][k]f[j][k]?
nnn头牛中选择jjj头牛并且总量为kkk的方案数, 是二维费用的背包问题

因此总的期望就是
∑i=1nf[j,k]×j!×(n−i−1)!n!j \sum_{i = 1}^{n} \frac{f[j, k] \times j! \times (n - i - 1)!}{n!} j i=1nn!f[j,k]×j!×(ni1)!j

将式子进行变形
∑i=1nj×f[j][k]nj!×(n−j−1)!(n−1)! \sum_{i = 1}^{n} \frac {j \times f[j][k]}{n} \frac {j! \times (n - j - 1)!}{(n - 1)!} i=1nnj×f[j][k](n1)!j!×(nj1)!

发现后面的式子是组合数倒数
∑i=1nj×f[j][k]n1Cn−1j \sum_{i = 1}^{n} \frac {j \times f[j][k]}{n}\frac {1}{C_{n - 1} ^ {j}} i=1nnj×f[j][k]Cn1j1
时间复杂度O(n4)O(n ^ 4)O(n4)

在枚举kkk时需要考虑加入当前aca_cac后超重因此ac+k>ma_c + k > mac+k>m并且k≤mk \le mkm

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long LL;
const int N = 55, M = N * N;

int n, m;
int w[N];
// f[i][j]表示从n头牛中选择i头牛并且总重量为j的所有方案
LL f[N][M];

// 计算组合数
LL C(int a, int b) {
   
	if (a < b) return 0;
	LL res = 1;
	for (int i = a, j = 1; j <= b; ++j, --i) {
   
		res = res * i / j;
	}

	return res;
}

int main() {
   
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n;
	int sum = 0;
	for (int i = 1; i <= n; ++i){
   
		cin >> w[i];
		sum += w[i];
	}
	cin >> m;

	// 汽车能装在全部的奶牛
	if (sum <= m) {
   
		cout << n << "\n";
		return 0;
	}

	double res = 0;
	// 枚举哪一头牛是超重的牛
	for (int c = 1; c <= n; ++c) {
   

		memset(f, 0, sizeof f);
		f[0][0] = 1;
		for (int i = 1; i <= n; ++i) {
   
			// 因为第c头奶牛超重
			if (i == c) continue;
			for (int j = i; j; --j) {
   
				for (int k = m; k >= w[i]; --k) {
   
					f[j][k] += f[j - 1][k - w[i]];
				}
			}
		}

		// 因为已经选择了一个奶牛 因此剩余奶牛数量是n - 1
		for (int j = 0; j < n; ++j) {
   
			for (int k = max(m - w[c] + 1, 0); k <= m; ++k) {
   
				double val = (double) f[j][k] / C(n - 1, j) / n * j;
				res += val;
			}
		}
	}

	cout << res << "\n";
	return 0;
}