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$,建立连接的代价是其权值之差
  • 需要最小化代价,并输出这个代价。

解题思路

这里就借用一下群里看到的一位大佬画的图吧,一共七种情况
avatat
我们可以观察到,只要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 * 105)$ 个点 $(x_i,y_i)(x_i,y_i \le 2 * 105)$ ,求出每个点,曼哈顿距离最小的非给定的 $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()"

上一篇 下一篇