GESP 六级¶
B3873 [GESP202309 六级] 小杨买饮料¶
提示
01背包
总容量不低于L,这不是01背包的典型模型
观察数据范围l<=2000, li <= 1e6,这有明显的冲突
2L的证明,是个难题
用刷表法,更好理解
代码
// 01背包
// 总容量不低于L,这不是01背包的典型模型
// 观察数据范围l<=2000, li <= 1e6,这有明显的冲突
// 2L的证明,是个难题
// 用刷表法,更好理解
#include <bits/stdc++.h>
using namespace std;
const int N = 510;
int n, L;
int dp[5010];
int a[N], b[N];
int main() {
cin >> n >> L;
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
b[i] = min(b[i], L);
}
memset(dp, 0x3f, sizeof dp);
dp[0] = 0;
for (int i = 1; i <= n; i++)
for (int j = 2 * L; j >= b[i]; j--)
dp[j] = min(dp[j], dp[j - b[i]] + a[i]);
int ret = 0x3f3f3f3f;
for (int j = L; j <= 2 * L; j++) ret = min(ret, dp[j]);
if (ret == 0x3f3f3f3f) cout << "no solution";
else cout << ret;
return 0;
}
B3874 [GESP202309 六级] 小杨的握手问题¶
思路
求每个数前面有几个数比自己小
求逆序对,是从小到大排列,a[i]>a[j], i...mid 这个范围都是比a[j]大的,都是逆序对
求正序对,是从大到小排序,a[i]<a[j], i...mid 这个范围都是比a[j]小的,都是正序对
代码
// 求每个数前面有几个数比自己小
// 求逆序对,是从小到大排列,a[i]>a[j], i...mid 这个范围都是比a[j]大的,都是逆序对
// 求正序对,是从大到小排序,a[i]<a[j], i...mid 这个范围都是比a[j]小的,都是正序对
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int a[N], tmp[N], n;
long long cnt;
void merge_sort(int l, int r) {
if (l >= r) return ;
int mid = l + r >> 1;
merge_sort(l, mid); merge_sort(mid + 1, r);
int i = l, j = mid + 1, k = 1;
while (i <= mid && j <= r) {
if (a[i] < a[j]) {
cnt += mid - i + 1;
tmp[k++] = a[j++];
}
else tmp[k++] = a[i++];
}
while (i <= mid) tmp[k++] = a[i++];
while (j <= r) tmp[k++] = a[j++];
for (int i = l, k = 1; i <= r; i++, k++) a[i] = tmp[k];
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
merge_sort(1, n);
// for (int i = 1; i <= n; i++) cout << a[i] << ' ';
// puts("");
cout << cnt;
return 0;
}
P10108 [GESP202312 六级] 闯关游戏¶
提示
每个关卡向后有M种选择,向后走ai关。离开这个关卡时,我们可以获得bi分。问离开时可以最多多少分
dp[i],刷表法,答案是dp[n],下标0-index。注意bi可能是负数
代码
// 每个关卡向后有M种选择,向后走ai关。离开这个关卡时,我们可以获得bi分。问离开时可以最多多少分
// dp[i],刷表法,答案是dp[n],下标0-index。注意bi可能是负数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4 + 10, M = 110;
int n, m, a[M], b[N];
ll dp[N];
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) cin >> a[i];
for (int i = 0; i < n; i++) cin >> b[i];
memset(dp, 0xcf, sizeof dp);
dp[0] = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++) {
int t = min(i + a[j], n);
dp[t] = max(dp[t], dp[i] + b[i]);
// printf("---%d %d %d\n", i, t, dp[t]);
}
cout << dp[n];
return 0;
}
P10109 [GESP202312 六级] 工作沟通¶
提示
n名员工,自己、父亲、父亲的父亲,可以领导自己
Q次询问,问可以领导所有人的员工编号(如有多种选择,选编号最大的)
代码
// n名员工,自己、父亲、父亲的父亲,可以领导自己
// Q次询问,问可以领导所有人的员工编号(如有多种选择,选编号最大的)
#include <bits/stdc++.h>
using namespace std;
const int N = 310;
int f[N], n, q, m;
map<int, int> mp;
void dfs(int x) {
mp[x]++;
if (x == 0) return ;
dfs(f[x]);
}
int main() {
cin >> n;
for (int i = 1; i <= n - 1; i++) cin >> f[i];
cin >> q;
while (q--) {
mp.clear();
cin >> m;
for (int i = 0; i < m; i++) {
int x;
cin >> x;
dfs(x);
}
int ret = 0;
for (auto x : mp) {
if (x.second == m) ret = max(ret, x.first);
}
cout << ret << '\n';
}
return 0;
}
P10376 [GESP202403 六级] 游戏¶
提示
从n->c, 每次可以-a,或者-b, 问有多少种方案
代码
```cpp
// 从n->c, 每次可以-a,或者-b, 问有多少种方案
// dp[i + a or b] += dp[i]; 答案是 dp[c]
// 这个题目的状态转移方程,debug了很久,没有理清楚c != i 这个逻辑,产生了重复
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, MOD = 1e9 + 7;
int n, a, b, c;
int dp[N];
int main() {
cin >> n >> a >> b >> c;
dp[n] = 1;
for (int i = n; i > c; i--) {
if (i - a >= c) dp[i - a] = (dp[i - a] + dp[i]) % MOD;
else dp[c] = (dp[c] + dp[i]) % MOD;
if (i - b >= c) dp[i - b] = (dp[i - b] + dp[i]) % MOD;
else dp[c] = (dp[c] + dp[i]) % MOD;
}
cout << dp[c];
return 0;
}
```
代码02
// 从n->c, 每次可以-a,或者-b, 问有多少种方案
// dp[i + a or b] += dp[i]; 答案是 dp[c]
// 枚举阶段的时候,只枚举到 i > c
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10, MOD = 1e9 + 7;
int n, a, b, c;
int dp[N];
int main() {
cin >> n >> a >> b >> c;
dp[n] = 1;
for (int i = n; i > c; i--) {
dp[max(i - a, c)] = (dp[max(i - a, c)] + dp[i]) % MOD;
dp[max(i - b, c)] = (dp[max(i - b, c)] + dp[i]) % MOD;
}
cout << dp[c];
return 0;
}
P10377 [GESP202403 六级] 好斗的牛¶
提示
每头牛的攻击范围是[ai, bi],攻击范围内不能有第二头牛。最少需要多长连续的牛棚可以把所有牛放下
观察到 n <= 9,这是一个排列枚举,维护最小值的问题
代码
// 每头牛的攻击范围是[ai, bi],攻击范围内不能有第二头牛。最少需要多长连续的牛棚可以把所有牛放下
// 观察到 n <= 9,这是一个排列枚举,维护最小值的问题
#include <bits/stdc++.h>
using namespace std;
pair<int, int> a[20];
int n;
bool vis[20];
vector<int> chosen;
int ret = 0x3f3f3f3f;
void upd() {
int p = 1;
for (int i = 0; i < n - 1; i++) {
int t = max(a[chosen[i]].second, a[chosen[i + 1]].first);
p += t + 1;
}
ret = min(ret, p);
return ;
}
void dfs(int u) {
if (u == n + 1) {
upd();
return ;
}
for (int i = 1; i <= n; i++) {
if (vis[i]) continue;
vis[i] = true;
chosen.push_back(i);
dfs(u + 1);
chosen.pop_back();
vis[i] = false;
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i].first;
for (int i = 1; i <= n; i++) cin >> a[i].second;
dfs(1);
cout << ret;
return 0;
}
P10721 [GESP202406 六级] 计算得分¶
代码
// 按官方给的题解思路
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[30], n, m;
string s;
int dp[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
cin >> m >> s;
for (int i = 1; i <= m; i++) {
dp[i] = dp[i - 1];
for (int j = 1; j <= n; j++) {
int l = i - j * 3 + 1;
if (l <= 0) break;
// 从后向前枚举,这一段连续都是abc
if (s.substr(l - 1, 3) == "abc") dp[i] = max(dp[i], dp[l] + a[j]);
else break;
}
}
cout << dp[m];
return 0;
}
P10722 [GESP202406 六级] 二叉树¶
提示
一颗二叉树,结点是0或者1
每次操作,会将这个结点的子树内的所有结点,在相反操作
q次操作后,所有结点的状态
n<=1e5, q<=1e5
代码
// 一颗二叉树,结点是0或者1
// 每次操作,会将这个结点的子树内的所有结点,在相反操作
// q次操作后,所有结点的状态
// n<=1e5, q<=1e5
// 把操作维护到所有的结点上,然后做dfs进行下放,利用奇偶性变更结点的状态
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
vector<int> h[N];
int n, q;
int op[N];
string s;
void dfs(int u, int fa) {
for (auto i : h[u]) {
if (i == fa) continue;
op[i] += op[u];
op[i] %= 2;
dfs(i, u);
}
}
int main() {
cin >> n;
for (int i = 2; i <= n; i++) {
int fa;
cin >> fa;
h[fa].push_back(i);
}
cin >> s >> q;
while (q--) {
int x;
cin >> x;
op[x] ++;
op[x] %= 2;
}
dfs(1, -1);
for (int i = 0; i < n; i++)
if (op[i + 1] % 2 == 1) {
if (s[i] == '0') s[i] = '1';
else s[i] = '0';
}
cout << s;
return 0;
}
代码02
// 一颗二叉树,结点是0或者1
// 每次操作,会将这个结点的子树内的所有结点,在相反操作
// q次操作后,所有结点的状态
// n<=1e5, q<=1e5
// 把操作维护到所有的结点上,然后做dfs进行下放,利用奇偶性变更结点的状态
// 没构造测试点,就直接交了,竟然直接AC了。用时14分钟
// 改成异或的方式
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
vector<int> h[N];
int n, q;
int op[N];
string s;
void dfs(int u, int fa) {
for (auto i : h[u]) {
if (i == fa) continue;
op[i] ^= op[u];
dfs(i, u);
}
}
int main() {
cin >> n;
for (int i = 2; i <= n; i++) {
int fa;
cin >> fa;
h[fa].push_back(i);
}
cin >> s >> q;
while (q--) {
int x;
cin >> x;
op[x] ^= 1;
}
dfs(1, -1);
for (int i = 0; i < n; i++)
if (op[i + 1] == 1) {
if (s[i] == '0') s[i] = '1';
else s[i] = '0';
}
cout << s;
return 0;
}
P11246 [GESP202409 六级] 小杨和整数拆分¶
提示
将n拆分成完全平方数之和,拆分个数越少越好
求方案数类型的dp
代码
// 将n拆分成完全平方数之和,拆分个数越少越好
// 求方案数类型的dp
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int f[N], n;
bool check(int n) {
int x = sqrt(n);
return x * x == n;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
f[i] = i;
if (check(f[i])) {
f[i] = 1;
continue;
}
// 看了题解,这里只需要 右边这部分的完全平方数
// 枚举的时候,是构造出来的完全平方数
for (int j = 1, len = sqrt(i); j <= len; j++)
f[i] = min(f[i], f[i - j * j] + 1);
}
cout << f[n];
return 0;
}
代码02
P11247 [GESP202409 六级] 算法学习¶
提示
70%数据,m, n <=9,可以利用两层循环,不断枚举
100%数据,m,n<=1e5,把每个算法放到一个开放链表里,每个链表按bi从大到小排序
代码
// 70%数据,m, n <=9,可以利用两层循环,不断枚举
// 100%数据,m,n<=1e5,把每个算法放到一个开放链表里,每个链表按bi从大到小排序
// 创建一个队列,每个<k的算法,入队。循环执行队列中,没有学完的算法(类似报数出队问题)
// 如果h[i]空了,但是第i种算法,还是小于k,肯定-1
// 用一个last来维护,上一次学习是什么算法,如果队列中只剩下一个元素的时候,last碰撞,就输出-1
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m, k;
vector<int> h[N];
struct node {
int a, b;
}p[N];
int last = -1;
int score[N];
int ret;
bool cmp(node a, node b) {
if (a.a != b.a) return a.a < b.a;
return a.b > b.b;
}
int main() {
cin >> m >> n >> k;
for (int i = 0; i < n; i++) cin >> p[i].a;
for (int i = 0; i < n; i++) cin >> p[i].b;
sort(p, p + n, cmp);
for (int i = 0; i < n; i++) h[p[i].a].push_back(p[i].b);
// for (int i = 1; i <= m; i++) {
// printf(":---%d\n", i);
// for (auto x : h[i]) cout << x << ' '; puts("");}
queue<int> q;
for (int i = 1; i <= m; i++) q.push(i);
bool flag = false;
while (!q.empty()) {
int t = q.front(); q.pop();
if (h[t].size() == 0 || t == last) {
flag = true;
break;
}
vector<int>::iterator it = h[t].begin();
score[t] += *it;
ret++;
last = t;
if (score[t] < k) q.push(t);
h[t].erase(it);
}
if (flag) cout << -1;
else cout << ret;
return 0;
}
代码02
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int m, n, k;
pair<int, int> a[N];
int b[N], c[N], maxn, cnt, ans;
bool flag;
int main() {
cin >> m >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i].second;
for (int i = 1; i <= n; i++) cin >> a[i].first;
// 按收益从小到大排序
sort(a + 1, a + 1 + n);
for (int i = n; i >= 1; i--) {
if (b[a[i].second] < k) {
b[a[i].second] += a[i].first;
if (b[a[i].second] >= k) cnt++;
ans++;
c[a[i].second]++;
maxn = max(maxn, c[a[i].second]);
}
}
if (cnt != m || maxn > (n + 1) / 2) cout << -1;
else cout << max(ans, maxn * 2 - 1); // 要么是用了多少题,要么是需要额外的题补充插空避免连续两道相同的题
return 0;
}
P11375 [GESP202412 六级] 树上游走¶
提示
二叉树,结点i的左儿子2i,右儿子2i+1
问移动n次后,位于哪个结点
向上和向下,走是会抵消的
代码
// 二叉树,结点i的左儿子2i,右儿子2i+1
// 问移动n次后,位于哪个结点
// 向上和向下,走是会抵消的
// 并且题目的最后,明确了,最终结果不会爆long long
// 所以要模拟这个抵消过程,而不是实际走的过程
#include <bits/stdc++.h>
using namespace std;
const long long INF = 1e12;
int main() {
long long n, i;
string s;
cin >> n >> i >> s;
int t = 0;
for (auto c : s) {
if (c == 'U') {
if (i / 2 >= 1) {
if (t) t--;
else i /= 2;
}
}
if (c == 'L') {
if (2 * i > INF) t++;
else i = 2 * i;
}
if (c == 'R') {
if (2 * i + 1> INF) t++;
else i = 2 * i + 1;
}
}
cout << i;
return 0;
}
代码02
// 题意
// 思路:超过1e12的时候,不要真的走
#include <bits/stdc++.h>
using namespace std;
const long long inf = 1e12;
long long n, p, cnt;
string s;
stack<char> stk;
int main() {
cin >> n >> p >> s;
for (auto c : s) {
if (c == 'U') {
if (!stk.empty()) stk.pop();
else {
if (p > 1) p /= 2;
}
}
if (c == 'L') {
if (p * 2 > inf) stk.push('L');
else p *= 2;
}
if (c == 'R') {
if (p * 2 + 1 > inf) stk.push('R');
else p = p * 2 + 1;
}
}
cout << p << '\n';
return 0;
}
P11376 [GESP202412 六级] 运送物资¶
提示
[A, B]两个城市之间有一些运输站,每个运输站有ci的容量限制
m 个货车,ai趟送A,bi趟送B
将这些货车,安排到这些运输站里,使得整体的运输成本最少
ai >= bi 的货车,放在一起,排序,差值越大的靠前,从左到右遍历分配
ai < bi 的货车,放在一起,排序,bi-ai 差值越大的靠前,从右到左分配
代码
// [A, B]两个城市之间有一些运输站,每个运输站有ci的容量限制
// m个货车,ai趟送A,bi趟送B
// 将这些货车,安排到这些运输站里,使得整体的运输成本最少
// ai >= bi 的货车,放在一起,排序,差值越大的靠前,从左到右遍历分配
// ai < bi 的货车,放在一起,排序,bi-ai 差值越大的靠前,从右到左分配
// 贪心思想
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 +10;
int n, m, x;
pair<int, int> p[N];
struct node {
int a, b, delta;
}A[N], B[N];
int idxa, idxb;
bool cmp(node a, node b) {
return a.delta > b.delta;
}
long long ret;
int main () {
cin >> n >> m >> x;
// 车站从左到右排好序
for (int i = 0; i < n; i++) cin >> p[i].first >> p[i].second;
sort(p, p + n);
// 这些货车排好序
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
if (a >= b) A[idxa++] = {a, b, a - b};
else B[idxb++] = {a, b, b - a};
}
sort(A, A + idxa, cmp);
sort(B, B + idxb, cmp);
int L = 0;
for (int i = 0; i < idxa; i++) {
if (p[L].second >= 1) {
ret += 1ll * A[i].a * p[L].first * 2;
ret += 1ll * A[i].b * (x - p[L].first) * 2;
p[L].second--;
}
else {
L++;
ret += 1ll * A[i].a * p[L].first * 2;
ret += 1ll * A[i].b * (x - p[L].first) * 2;
p[L].second--;
}
}
int R = n - 1;
for (int i = 0; i < idxb; i++) {
if (p[R].second >= 1) {
ret += 1ll * B[i].a * p[R].first * 2;
ret += 1ll * B[i].b * (x - p[R].first) * 2;
p[R].second--;
}
else {
R--;
ret += 1ll * B[i].a * p[R].first * 2;
ret += 1ll * B[i].b * (x - p[R].first) * 2;
p[R].second--;
}
}
cout << ret;
return 0;
}
P11962 [GESP202503 六级] 树上漫步¶
提示
一棵树,从一个结点出发,走偶数步,可以结束漫步过程
问从每个结点出发,可以结束漫步的结点,有多少个
代码
// 一棵树,从一个结点出发,走偶数步,可以结束漫步过程
// 问从每个结点出发,可以结束漫步的结点,有多少个
// n<=2e5
// 对于n<=1e3的数据,我们可以从每个结点,进行一遍bfs,维护到其他点的距离,如果是偶数的,就是可以结束漫步的
// 这是n^2的
// 我们可以维护,每个结点到根结点的距离,作为共同的祖先。看到祖先的距离差值,是不是偶数
// 用bfs维护出最短路,距离是奇数有多少,距离偶数有多少 答案就有了
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
vector<int> h[N];
int n, d[N], odd, even;
void bfs() {
memset(d, 0x3f, sizeof d);
queue<int> q;
q.push(1);
d[1] = 0;
while (!q.empty()) {
int t = q.front(); q.pop();
for (auto u : h[t]) {
if (d[u] != 0x3f3f3f3f) continue;
d[u] = d[t] + 1;
q.push(u);
}
}
}
int main() {
cin >> n;
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
h[a].push_back(b); h[b].push_back(a);
}
bfs();
for (int i = 1; i <= n; i++)
if (d[i] % 2) odd++;
else even++;
for (int i = 1; i <= n; i++)
if (d[i] % 2) cout << odd << ' ';
else cout << even << ' ';
return 0;
}
P11963 [GESP202503 六级] 环线¶
提示
不跨环的情况,就是[1..n]的最大字段和
跨环的情况,两头是选的,中间有一部分是不选的。那么就是[1..n]的和,减去,中间的最小子段和
代码
// 不跨环的情况,就是[1..n]的最大字段和
// 跨环的情况,两头是选的,中间有一部分是不选的。那么就是[1..n]的和,减去,中间的最小子段和
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef long long ll;
ll a[N], n;
ll maxn = -1e18, minn = 1e18;
ll total;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
total += a[i];
}
ll sum = 0, sum2 = 0;
for (int i = 1; i <= n; i++) {
sum = max(sum + a[i], a[i]);
maxn = max(maxn, sum);
sum2 = min(sum2 + a[i], a[i]);
minn = min(minn, sum2);
}
cout << max(maxn, total - minn == 0 ? maxn: total - minn);
return 0;
}
代码02
// 单调队列
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
typedef long long ll;
ll a[N * 2], s[N * 2], n;
int q[N * 2], l, r;
ll ans = -1e18;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i + n] = a[i];
}
for (int i = 1; i <= n * 2; i++) s[i] = s[i - 1] + a[i];
// q[....] 存放的是,
// 从小到大,i之前的比s[i]都小的前缀和
// 单调的
for (int i = 1; i <= n * 2; i++) {
while (l <= r && i - q[l] > n) l++; // 左边滑动出去的部分
ans = max(ans, s[i] - s[q[l]]);
while (l <= r && s[q[r]] > s[i]) r--; // 右边不符合需求的前缀和,吐掉
q[++r] = i; // 入队
}
cout << ans;
return 0;
}
P13015 [GESP202506 六级] 学习小组¶
提示
两种dp的写法,都要理解一下。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int a[N], n, dp[N]; // dp[i]表示i个人可以获得的最大价值
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
// 线性dp
for (int i = 1; i <= n; i++)
for (int j = 0; j < i; j++)
dp[i] = max(dp[i], dp[j] + a[i - j]);
cout << dp[n];
return 0;
}
代码02
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int v[N], w[N], n, dp[N]; // 完全背包
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> v[i], w[i] = i;
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= n; j++)
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
cout << dp[n];
return 0;
}
P13016 [GESP202506 六级] 最大因数¶
代码
// 每次向上爬一个结点,这个结点就是自己的最大因数
// 模拟可得60pts
#include <bits/stdc++.h>
using namespace std;
bool is_prime(int n) {
if (n < 2) return false;
for (int i = 2; i <= n / i; i++)
if (n % i == 0) return false;
return true;
}
int main() {
int q;
cin >> q;
while (q--) {
int x, y;
cin >> x >> y;
int step = 0;
// 找x,y的最大公约数
while (x != y) {
if (x > y) swap(x, y);
if (is_prime(y)) {
step++;
y = 1;
continue;
}
for (int i = 2; ; i++)
if (y % i == 0) {
step++;
y = y / i;
break;
}
}
cout << step << '\n';
}
return 0;
}
代码02
// 官方题解思路
// 把x,y进行分解因子,放到一个数组里
// x = 12
// 12 2 2 3
// 6 3 1
// 下面这一行,b[i] = a[i - 1] / a[i], 这个b[i]就是父结点
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int a[N], b[N];
int prime[N];
void f(int x, int a[]) {
a[0] = x;
int idx = 0;
for (int i = 2; i <= x / i; i++)
while (x % i == 0) prime[++idx] = i, x /= i;
if (x > 1) prime[++idx] = x;
// 存向上的父结点链路,向上爬
for (int i = 1; i <= idx; i++) a[i] = a[i - 1] / prime[i];
}
int main() {
int q;
cin >> q;
while (q--) {
int x, y;
cin >> x >> y;
f(x, a);
f(y, b);
int i = 0, j = 0;
while (a[i] != b[j]) {
if (a[i] > b[j]) i++;
else j++;
}
cout << i + j << '\n';
}
return 0;
}
P14075 [GESP202509 六级] 划分字符串¶
提示
dp[i]表示以i结尾可以获得的最大价值
O(n*26),每次从i的位置向前找可以切分的子串长度,虽然是两层循环
但是因为都是小写字母,最多循环26次就肯定会重复
代码
// dp[i]表示以i结尾可以获得的最大价值
// O(n*26),每次从i的位置向前找可以切分的子串长度,虽然是两层循环
// 但是因为都是小写字母,最多循环26次就肯定会重复
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
long long dp[N];
int a[N], n;
string s;
int main() {
cin >> n >> s;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
map<char, int> mp;
// j枚举的是右边子串的第一个位置
for (int j = i; j >= 1; j--) {
if (mp.count(s[j - 1])) break;
mp[s[j - 1]] = 1;
dp[i] = max(dp[i], dp[j - 1] + a[i - j + 1]);
}
}
cout << dp[n];
return 0;
}
代码02
// dp[i]表示以i结尾可以获得的最大价值
// O(n*26),每次从i的位置向前找可以切分的子串长度,虽然是两层循环
// 但是因为都是小写字母,最多循环26次就肯定会重复
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
long long dp[N];
int a[N], n;
string s;
int main() {
cin >> n >> s;
for (int i = 1; i <= n; i++) cin >> a[i];
s = ' ' + s;
dp[1] = a[1];
for (int i = 1; i <= n; i++) {
map<char, int> mp;
for (int j = i - 1; j >= 1; j--) {
if (mp.count(s[j])) break;
mp[s[j]] = 1;
dp[i] = max(dp[i], dp[j] + a[i - j]);
}
}
cout << dp[n];
return 0;
}
P14076 [GESP202509 六级] 货物运输¶
提示
重新思考题意,我们发现这是一颗树
一颗树有很多分支,分支最长的那条,只走一遍。其他分支走两遍
代码
// 重新思考题意,我们发现这是一颗树
// 一颗树有很多分支,分支最长的那条,只走一遍。其他分支走两遍
// 答案就是 所有边长的和*2-最长的那边分支
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
vector<pair<int, int>> h[N];
int n;
ll sum;
bool vis[N];
set<int> st;
ll maxn;
void dfs(int u, ll len) {
int cnt = 0;
for (auto x : h[u]) {
if (!vis[x.first]) {
cnt++;
vis[x.first] = true;
dfs(x.first, len + x.second);
vis[x.first] = false;
}
}
if (cnt == 0) maxn = max(maxn, len);
}
int main() {
cin >> n;
for (int i = 1; i < n; i++) {
int u, v, l;
cin >> u >> v >> l;
h[u].push_back({v, l});
h[v].push_back({u, l});
sum += l;
}
vis[1] = true;
dfs(1, 0);
cout << sum * 2 - maxn;
return 0;
}