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(n2logn)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质因数的个数是logai\log{}{a_i}logai, 与aia_iai不互质的数的位置一定至少与aia_iai有一个公共的质因子
原来是暴力枚举所有非互质数, 现在可将整个集合进行划分, 划分成与aia_iai有相同的质因子是p1p_1p1的数, 是p2p_2p2的数…
g[i]g[i]g[i]代表所有包含质因子iii的f[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(nlogn)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 - 1n−j−1头牛, 在对重量进行划分
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!×(n−j−1)!
如何计算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=1∑nn!f[j,k]×j!×(n−i−1)!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=1∑nnj×f[j][k](n−1)!j!×(n−j−1)!
发现后面的式子是组合数倒数
∑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=1∑nnj×f[j][k]Cn−1j1
时间复杂度O(n4)O(n ^ 4)O(n4)
在枚举kkk时需要考虑加入当前aca_cac后超重因此ac+k>ma_c + k > mac+k>m并且k≤mk \le mk≤m
#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;
}

京公网安备 11010502036488号