A. Playoff
解题思路
可以观察到在第一轮所有偶数就已经被淘汰了
后面的所有轮的两个数相加都为偶数,所以输出较大的那个奇数
而较大的那个奇数就是(1 << x) - 1
Code
void solve()
{
int x; cin >> x;
cout << ((1 << x) - 1) << endl; // 等价于2 ^ x - 1, 不过位运算更快些
}
B. Prove Him Wrong
解题思路
题目说,要求总和不会变小
所以我们可以列出一个式子:
首先假设a[j] >= a[i];
a[j] + a[i] <= 2(a[j] - a[i]) -> a[j] >= 3a[i]
由此可得后一个数最少也得是前一个的3倍,将总和相加,如果超出范围则输出NO
Code
void solve()
{
int n; cin >> n;
a[1] = 1;
for(int i = 2; i <= n;i++)
{
a[i] = a[i-1] * 3;
if(a[i] > 0x3f3f3f3f) // 超出范围
{
puts("NO");
return;
}
}
puts("YES");
for(int i = 1; i <= n;i++) cout << a[i] << " ";
cout << endl;
}
C. Fault-tolerant Network (枚举)
题目大意
题意:现有两排计算机,每排计算机的第$i$台与第$i + 1$台之间有连接。
现需要额外建立若干连接,连接应当满足以下条件,且使得即使有一台计算机失效,其余任意两台计算机也是直接或间接的连接的(即无向图中没有割点)。
- 只能在第一排的某计算机和第二排的某计算机之间建立连接。
- 计算机有一个权值 $w$,建立连接的代价是其权值之差
- 需要最小化代价,并输出这个代价。
解题思路
这里就借用一下群里看到的一位大佬画的图吧,一共七种情况
我们可以观察到,只要a[1], a[n], b[1], b[n],有额外连接,就可以满足条件
一共两条线2种,3条线3种,4条线2种 = 7种情况, 对于每种情况取最小值
我们可以先计算出四个关键点在对面的最小权值差(枚举), 然后对于其他的特殊情况进行特殊对比
Code
void solve()
{
int n; cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
// 这里是两条线的情况,单独处理
int res = min(abs(a[1] - b[1]) + abs(a[n] - b[n]), abs(a[1] - b[n]) + abs(a[n] - b[1]));
int d1 = 1e9, d2 = 1e9, d3 = 1e9, d4 = 1e9;
for (int i = 1; i <= n; i++) // 计算出四个关键点在对面的最小权值差
{
d1 = min(d1, abs(b[i] - a[1]));
d2 = min(d2, abs(b[i] - a[n]));
d3 = min(d3, abs(b[1] - a[i]));
d4 = min(d4, abs(b[n] - a[i]));
}
res = min({res, d1 + d2 + d3 + d4, abs(a[1] - b[1]) + d2 + d4,
abs(a[1] - b[n]) + d2 + d3, abs(b[1] - a[n]) + d1 + d4, abs(b[n] - a[n]) + d1 + d3});
cout << res << endl;
}
D. Nearest Excluded Points(BFS)
题目大意
给定 $n(n \le 2 * 10^5)$ 个点 $(x_i,y_i)(x_i,y_i \le 2 * 10^5)$ ,求出每个点,曼哈顿距离最小的非给定的 $n$ 个点的一个点。
解题思路
一道BFS,不过要做些“手脚”
一般BFS我们都是将源点入队,但是这题我们要反着来,将源点周围的四个点入队,
然后让它们扩展,如果在过程中扩展到的点是在输入中给出的,说明此时扩展到的这个点是距离给出点最近的一点
问:为什么这样做是可行的,并且可以实现最短距离?
因为我们不断地在更新每个点是从哪个点过来的,距离一直在变(详细看代码)
Code
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
int dx[4]= {1,0,-1,0},dy[4]= {0,1,0,-1};
const int N = 200010;
int n, x[N], y[N];
PII res[N];
map<PII, int> id, vis;
map<PII, PII> root; // root[]用来记录祖先结点
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> x[i] >> y[i];
id[{x[i], y[i]}] = i;
}
queue<PII> q;
for (int i = 1; i <= n; i++)
for (int j = 0; j < 4; j++)
{
int xx = x[i] + dx[j], yy = y[i] + dy[j];
if (!id.count({xx, yy})) // 不能加入给定点
{
q.push({xx, yy});
root[{xx,yy}] = {xx, yy}; // 这里是起点,起点没有前驱节点,所以是自己
}
}
while (q.size())
{
auto t = q.front(); q.pop();
for (int i = 0; i < 4; i++)
{
int tx = t.first, ty = t.second;
int xx = tx + dx[i], yy = ty + dy[i];
if (id.count({xx, yy}) && !vis.count({xx,yy})) // 只有扩展到了给定点并且未访问过,才加入队列,并更新距离
{
vis[{xx,yy}] = 1; // 这里设置访问过的好处就是避免重复,并且BFS是一层一层扩展的,在前面被遍历的一定是比较短的
root[{xx, yy}] = root[{tx,ty}]; // 祖先结点的转移,方便下一次扩展
res[id[{xx,yy}]] = root[{xx,yy}];
q.push({xx,yy});
}
}
}
for (int i = 1; i <= n; i++) cout << res[i].first << ' ' << res[i].second << endl;
return 0;
}
PS: 本人代码头文件中带有”define int long long”,
如出现wa可能是精度问题, 使用上述头文件需将”int main()” 改为”signed main()”!
前排%佬
%%