Acwing161周赛题解

A

#include 
#include 
#include 

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 ( n 2 log ⁡ n ) O(n ^ 2\log{}{n}) O(n2logn), 会超时

#include 
#include 
#include 

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;
}

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

原来是暴力枚举所有非互质数, 现在可将整个集合进行划分, 划分成与 a i a_i ai有相同的质因子是 p 1 p_1 p1的数, 是 p 2 p_2 p2的数…

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

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

#include 
#include 
#include 

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 ≠ 0 j \ne 0 j=0, 因为 0 0 0没有最小质因子

C

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

Acwing161周赛题解_第1张图片
将集合继续进行划分, 前面有 j j j头牛, 后面有 n − j − 1 n - j - 1 nj1头牛, 在对重量进行划分
f [ i ] [ j ] f[i][j] f[i][j]表示选择了 j j j头牛并且总重量是 k k k的方案数, 发生的概率就是 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]?
n n n头牛中选择 j j j头牛并且总量为 k k k的方案数, 是二维费用的背包问题

因此总的期望就是
∑ i = 1 n f [ 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 = 1 n j × f [ j ] [ k ] n j ! × ( 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 = 1 n j × f [ j ] [ k ] n 1 C n − 1 j \sum_{i = 1}^{n} \frac {j \times f[j][k]}{n}\frac {1}{C_{n - 1} ^ {j}} i=1nnj×f[j][k]Cn1j1
时间复杂度 O ( n 4 ) O(n ^ 4) O(n4)

在枚举 k k k时需要考虑加入当前 a c a_c ac后超重因此 a c + k > m a_c + k > m ac+k>m并且 k ≤ m k \le m km

#include 
#include 
#include 

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;
}

你可能感兴趣的:(算法,笔记,c++)