传送门
A - Cut
原题
限制
输入
输出
测试样例
input1
5 3
1 2 3 4 5
output1
3 4 5 1 2最初,写在卡片上的整数从上到下为 1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5 。
从牌堆底部取出三张牌放在上面后,写在牌上的整数从上到下变为 3 , 4 , 5 , 1 , 2 3,4,5,1,2 3,4,5,1,2 。
input2
6 2
1 2 1 2 1 2
output2
1 2 1 2 1 2
分析
- 这题可以纯暴力,有个比较简单的方法是先把前 n − k n - k n−k个元素翻转,再把后 k k k 个元素翻转,然后整体翻转即可,要注意边界。
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for(int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::reverse(a.begin(), a.begin() + n - k);
std::reverse(a.begin() + n - k, a.end());
std::reverse(all(a));
for(auto x : a) {
std::cout << x << " ";
}
return 0;
}
B - Decrease 2 max elements
原题
限制
输入
输出
测试样例
input1
4
1 2 3 3
output1
4
input2
3
1 1 100
output2
2
分析
- 这题同样可以纯暴力,赛时没有去想比较简单的方法
- 每次操作之前进行排序,并对数组中仍为正整数的个数计数,只要个数 ≥ 2 \ge 2 ≥2,那么就需要进行操作,每次把操作的次数 + 1 +1 +1即可
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n;
std::cin >> n;
std::vector<int> a(n);
for(int i = 0; i < n; i++) {
std::cin >> a[i];
}
int cnt = n;
int ans = 0;
while(cnt >= 2) {
std::sort(all(a), std::greater());
a[0]--;
a[1]--;
if(a[0] <= 0) {
cnt--;
}
if(a[1] <= 0) {
cnt--;
}
ans++;
}
std::cout << ans;
return 0;
}
C - Triple Attack
原题
限制
输入
输出
测试样例
input1
3
6 2 2
output1
8
input2
9
1 12 123 1234 12345 123456 1234567 12345678 123456789
output2
82304529
input3
5
1000000000 1000000000 1000000000 1000000000 1000000000
output3
3000000000
分析
- 记答案为 a n s ans ans,操作次数与要减的数有个规律,当 a n s m o d 3 = = 0 ans _{mod 3} == 0 ansmod3==0时,此时进行的是 − 3 -3 −3 操作,否则是 − 1 -1 −1 操作
- 假设当前要操作的数为 a i a_{i} ai,由于减数是每3个一循环,每个循环可以减去5,因此统计 a i a_{i} ai进行 − 5 -5 −5的操作次数,然后再根据第 a n s ans ans次操作进行 − 1 -1 −1 或 − 3 -3 −3 操作
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n;
std::cin >> n;
i64 ans = 0;
for(int i = 0; i < n; i++) {
int m;
std::cin >> m;
ans += m / 5 * 3;
m %= 5;
while(m > 0) {
ans++;
if(ans % 3 == 0) {
m -= 3;
} else {
m--;
}
}
}
std::cout << ans;
return 0;
}
D - Minimum Steiner Tree
原题
限制
输入
输出
测试样例
input1
7 3
1 2
1 3
2 4
2 5
3 6
3 7
1 3 5
output1
4
input2
4 4
3 1
1 4
2 1
1 2 3 4
output2
4
input3
5 1
1 4
2 3
5 2
1 2
1
output3
1
分析
- 由于题给的是一颗树,每个节点只有一个父亲节点,所以我们可以这样想,假设当前来到节点 a i a_{i} ai,只有当当前节点的上级节点和当前节点的下级节点有需要加入新树的点,这个点才需要加入新树,因为只有这样这两个点才能连在一颗树上
- 这种由下级节点影响上级节点的思路我们可以用树形dp来做
- 以 0 0 0 号节点为根节点,数组 d p i dp_{i} dpi 表示以节点 i i i 为根节点时有多少个节点加入了新树,再用数组 v v v 标记需要加入新树的点
#include<bits/stdc++.h>
#define all(a) a.begin(), a.end()
using i32 = int;
using u32 = unsigned int;
using i64 = long long;
using u64 = unsigned long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n, k;
std::cin >> n >> k;
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
std::cin >> u >> v;
u--;
v--;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<int> v(n);
for(int i = 0; i < k; i++) {
int m;
std::cin >> m;
m--;
v[m] = 1;
}
std::vector<int> dp(n);
int ans = 0;
auto dfs = [&](auto self, int u, int fa) -> void {
dp[u] = v[u];
int c = 0;
if(v[u]) {
c = 2;
}
for(auto v : adj[u]) {
if(v == fa) {
continue;
}
self(self, v, u);
dp[u] += dp[v];
if(dp[v] > 0) {
c++;
}
}
if(dp[u] < k) {
c++;
}
if(c >= 2) {
ans++;
}
};
dfs(dfs, 0, -1);
std::cout << ans;
return 0;
}