A
题意
2 n 2^n 2n名运动员比赛,当运动员 x 和 y 比赛时,获胜者的决定如下:
- 如果 x + y x+y x+y是奇数,则编号较低的运动员获胜
- 如果 x + y x+y x+y是偶数,则编号较高的运动员获胜。
思路
最后肯定是最大的奇数获胜,也就是 2 n − 1 2^n - 1 2n−1
AC代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while(t--) {
int n;
cin >> n;
cout << (1 << n) - 1 << '\n';
}
return 0;
}
B
题意
有一个数组,可以做一次下面的操作
- 选择两个不同的下标 i , j i,j i,j
- 令 a i = a j = ∣ a i − a j ∣ a_i = a_j = |a_i - a_j| ai=aj=∣ai−aj∣
你需要构造一个数组 a , a i ∈ [ 1 , 1 0 9 ] a,a_i∈[1,10^9] a,ai∈[1,109],使得对于任意的 i , j i,j i,j都有执行完上边的操作后,数组的和不会变小。
思路
数组 [ 1 , 3 , 9 , 27.... 3 n ] [1, 3, 9, 27....3^n] [1,3,9,27....3n]即为最优解,我们只需要判断 3 n 3^n 3n是否在 1 0 9 10^9 109之内就可以确定是否有解
AC代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
ll tmp = 1;
vector<ll> p;
for(int i = 1;tmp <= 1e9;tmp *= 3) p.push_back(tmp);
int t;
cin >> t;
while(t--) {
int n;
cin >> n;
if(n > p.size()) cout << "NO" << '\n';
else {
cout << "YES" << '\n';
for(int i = 0;i < n;i++) cout << p[i] << ' ';cout << '\n';
}
}
return 0;
}
C
题意
最开始有两排电脑,他们的权值分别是 a i , b i a_i,b_i ai,bi,对于每排电脑,相邻的两个电脑已经被电线连接,我们可以任选两台电脑然后用电线给他进行连接,需要的代价为 ∣ a i − b i ∣ |a_i - b_i| ∣ai−bi∣,求最小的连线代价,使得在破坏任意一条电线后,两排电脑属于一个连通块内。
思路
不难发现,只有两排电脑的最左端和右端的电脑需要被连接,所以最少可以需要两条电线,最多需要四条电线,所以可以暴力枚举
AC代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while(t--) {
int n;
cin >> n;
vector<int> a(n + 1), b(n + 1);
for(int i = 1;i <= n;i++) cin >> a[i];
for(int i = 1;i <= n;i++) cin >> b[i];
auto dis = [&](int x, int y) -> ll {
return abs(a[x] - b[y]);
};
auto cal = [&](int x, int y) -> ll {
ll ret = 0x3f3f3f3f3f3fll;
if(x) {
for(int i = 1;i <= n;i++) ret = min(ret, dis(x, i));
}
else {
for(int i = 1;i <= n;i++) ret = min(ret, dis(i, y));
}
return ret;
};
ll ans = 0x3f3f3f3f3f3f3fll;
ans = min(ans, dis(1, n) + dis(n, 1));
ans = min(ans, dis(1, 1) + dis(n, n));
ans = min(ans, dis(1, 1) + cal(n, 0) + cal(0, n));
ans = min(ans, dis(1, n) + cal(n, 0) + cal(0, 1));
ans = min(ans, dis(n, 1) + cal(1, 0) + cal(0, n));
ans = min(ans, dis(n, n) + cal(0, 1) + cal(1, 0));
ans = min(ans, cal(1, 0) + cal(0, 1) + cal(n, 0) + cal(0, n));
cout << ans << '\n';
}
return 0;
}
D
题意
给出 n n n个点,对于每个点,找到一个离该点最近(曼哈顿距离)的且不在着n个点中的点。
思路
- 从答案开始搜
- 将离每个点曼哈顿为 1 1 1的点(不在这 n n n个点中)压入队列,进行 B F S BFS BFS,扫到属于 n n n个点中的点就可以确定这个点的答案了。
AC代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
map<pair<int, int>, int> rk;
set<pair<int, int> > st;
vector<int> x(n + 1), y(n + 1);
vector<pair<int, int> > ans(n + 1);
for(int i = 1;i <= n;i++) {
cin >> x[i] >> y[i];
rk[pair<int, int>(x[i], y[i])] = i;
}
for(int i = 1;i <= n;i++) {
st.insert({x[i] + 1, y[i]});
st.insert({x[i] - 1, y[i]});
st.insert({x[i], y[i] + 1});
st.insert({x[i], y[i] - 1});
}
queue<array<int, 4> > q;
for(auto [x, y] : st) {
if(rk.count({x, y}) == 0) {
q.push({x, y, x, y});
}
}
while(q.size()) {
auto [x, y, ax, ay] = q.front();
q.pop();
for(int i = 0;i < 4;i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if(rk.count({nx, ny})) {
ans[rk[pair<int, int>(nx, ny)]] = {ax, ay};
rk.erase(pair<int, int>(nx, ny));
q.push({nx, ny, ax, ay});
}
}
}
for(int i = 1;i <= n;i++) cout << ans[i].first << ' ' << ans[i].second << '\n';
return 0;
}
总结
前三道题开的还是很顺利,第四题想了一个很逆天难写的思路,和队友写了一个小时,最后wa2,还是太菜了,不摆了,写题!