跳转至

ABC385(A-F)

A - Equally

题目大意

给你三个整数 \(A\)\(B\)\(C\),判断能否将这三个数字分成 \(2\) 组或 \(3\) 组,使得每个组的数字之和相等。

参考代码
#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> a(3);
    cin >> a[0] >> a[1] >> a[2];
    sort(a.begin(), a.end());
    if ((a[0] == a[1] && a[1] == a[2]) || a[0] + a[1] == a[2])
        cout << "Yes" << endl;
    else
        cout << "No" << endl;
    return 0;
}

B - Santa Claus 1

题目大意

有一个 \(H \times W\) 的网格,如果某个格子是 # 表示障碍物,. 表示空地,@ 表示房子。障碍物不可通行,空地和房子可以通行。

初始的时候,圣诞老人在 \((X,Y)\) 位置。给你一个字符串 \(T\) 用来表示圣诞老人的行动,假设圣诞老人当前的位置是 \((x, y)\),圣诞老人会根据 \(T\) 的内容执行以下移动:

  • 如果 \(T_i\)U,且 \((x-1, y)\) 可通行,则移动到 \((x-1, y)\)
  • 如果 \(T_i\)D,且 \((x+1, y)\) 可通行,则移动到 \((x+1, y)\)
  • 如果 \(T_i\)L,且 \((x, y-1)\) 可通行,则移动到 \((x, y-1)\)
  • 如果 \(T_i\)R,且 \((x, y+1)\) 可通行,则移动到 \((x, y+1)\)

圣诞老人每经过一个房子就会送出礼物,每个房子至多送出一次礼物,问:执行完所有行动之后,圣诞老人送出了多少次礼物?

参考代码
#include <iostream>
#include <set>
#include <string>
#include <utility>
#include <vector>

using namespace std;

int main()
{
    int h, w, x, y;
    cin >> h >> w >> x >> y;
    x--, y--;
    vector<string> g(h);
    for (auto &s : g)
        cin >> s;
    string t;
    cin >> t;
    set<pair<int, int>> vis;

    auto in_lim = [h, w](int r, int c) -> bool
    { return r >= 0 && r < h && c >= 0 && c < w; };

    for (auto ch : t)
    {
        int nx = x, ny = y;
        if (ch == 'U')
            nx--;
        else if (ch == 'D')
            nx++;
        else if (ch == 'L')
            ny--;
        else
            ny++;
        if (!in_lim(nx, ny) || g[nx][ny] == '#')
            continue;
        x = nx;
        y = ny;
        if(g[x][y] == '@')
            vis.insert({x, y});
    }
    cout << x + 1 << ' ' << y + 1 << ' ' << vis.size() << endl;
    return 0;
}

C - Illuminate Buildings

题目大意

\(N(1 \le N \le 3000)\) 个建筑等间距的放置在数轴上,第 \(i\) 个建筑的高度是 \(H_i(1 \le H_i \le 3000)\)

你打算挑选一些建筑,对这些建筑进行装饰,被挑选的建筑需要满足以下所有条件:

  • 被挑选的建筑的高度都相同。
  • 被挑选的建筑的间距都相同。

问:最多能选多少个建筑进行装饰?(注:挑选 \(1\) 个建筑也满足以上条件。)

解题思路

定义 \(dp[i][j]\) 表示以建筑 \(i\) 结尾且间距为 \(j\) 的高度相等的序列的最大序列长度,因为选单个也合法,因此所有元素的初始值为 \(dp[i][j]=1\),更新式为 \(dp[i][j] = \max\limits_{j=1 \land H_i = H_{i-j}}^{i-1}(dp[i][j], dp[j][i-j]+1)\)

参考代码
#include <iostream>
#include <vector>

using namespace std;

int main()
{
    int n;
    cin >> n;
    vector<int> h(n);
    vector<vector<int>> dp(n, vector<int>(n));
    int ans = 1;
    for(int i = 0; i < n; i++)
    {
        cin >> h[i];
        dp[i].assign(n, 1);
        for(int j = 0; j < i; j++)
            if(h[i] == h[j])
            {
                dp[i][i-j] = dp[j][i-j] + 1;
                ans = max(ans, dp[i][i-j]);
            }
    }
    cout << ans << endl;
    return 0;
}

D - Santa Claus 2

题目大意

有一个无限大的二维平面,在平面上有 \(N(1 \le N \le 2 \times 10 ^ 5)\) 个房子,其中第 \(i\) 个房子的坐标是 \((X_i, Y_i)\)。初始的时候,圣诞老人在 \((S_x, S_y)\),圣诞老人会执行 \(M(1 \le M \le 2 \times 10 ^ 5)\) 次行动,每一步行动由 \((D_i, C_i)\) 描述。

对于第 \(i\) 次行动,假设在行动前圣诞老人的位置是 \((x, y)\),则:

  • 如果 \(D_i\)U,表示圣诞老人从 \((x, y)\) 开始向上走 \(C_i\) 格,并经过中间的房子,最终走到 \((x, y+C_i)\)
  • 如果 \(D_i\)D,表示圣诞老人从 \((x, y)\) 开始向下走 \(C_i\) 格,并经过中间的房子,最终走到 \((x, y-C_i)\)
  • 如果 \(D_i\)L,表示圣诞老人从 \((x, y)\) 开始向左走 \(C_i\) 格,并经过中间的房子,最终走到 \((x-C_i, y)\)
  • 如果 \(D_i\)R,表示圣诞老人从 \((x, y)\) 开始向右走 \(C_i\) 格,并经过中间的房子,最终走到 \((x+C_i, y)\)

圣诞老人每经过一个房子就会送出礼物,每个房子至多送出一次礼物,问:执行完所有行动之后,圣诞老人送出了多少次礼物?

解题思路

set 存储每一行和每一列有哪些房子,移动的时候在 set 上更新就好。和 ABC370 D题 一个做法。

参考代码
#include <iostream>
#include <set>
#include <unordered_map>

using namespace std;
using LL = long long;

int main()
{
    int n, m;
    LL x, y;
    cin >> n >> m >> x >> y;
    unordered_map<LL, set<LL>> row, col;
    while (n--)
    {
        int hx, hy;
        cin >> hx >> hy;
        col[hx].insert(hy);
        row[hy].insert(hx);
    }
    int cnt = 0;
    while (m--)
    {
        char d;
        int c;
        cin >> d >> c;
        LL nx = x, ny = y;
        if (d == 'U')
        {
            ny += c;
            auto it = col.find(x);
            if (it != col.end())
            {
                set<LL> &xs = it->second;
                auto set_it = xs.lower_bound(y);
                while (set_it != xs.end())
                {
                    int hy = *set_it;
                    if (hy > ny)
                        break;
                    cnt++;
                    row[hy].erase(x);
                    set_it = xs.erase(set_it);
                }
            }
            y = ny;
        }
        else if (d == 'D')
        {
            ny -= c;
            auto it = col.find(x);
            if (it != col.end())
            {
                set<LL> &xs = it->second;
                auto set_it = xs.lower_bound(ny);
                while (set_it != xs.end())
                {
                    int hy = *set_it;
                    if (hy > y)
                        break;
                    cnt++;
                    row[hy].erase(x);
                    set_it = xs.erase(set_it);
                }
            }
            y = ny;
        }
        else if (d == 'L')
        {
            nx -= c;
            auto it = row.find(y);
            if (it != col.end())
            {
                set<LL> &xs = it->second;
                auto set_it = xs.lower_bound(nx);
                while (set_it != xs.end())
                {
                    int hx = *set_it;
                    if (hx > x)
                        break;
                    cnt++;
                    col[hx].erase(y);
                    set_it = xs.erase(set_it);
                }
            }
            x = nx;
        }
        else
        {
            nx += c;
            auto it = row.find(y);
            if (it != col.end())
            {
                set<LL> &xs = it->second;
                auto set_it = xs.lower_bound(x);
                while (set_it != xs.end())
                {
                    int hx = *set_it;
                    if (hx > nx)
                        break;
                    cnt++;
                    col[hx].erase(y);
                    set_it = xs.erase(set_it);
                }
            }
            x = nx;
        }
    }
    cout << x << ' ' << y << ' ' << cnt << endl;
    return 0;
}

E - Snowflake Tree

题目大意

一颗雪花树的定义以及生成方式如下:

  1. 选定两个正整数 \(x\)\(y\)

  2. 放置一个节点(下图红色的节点)。

  3. 放置 \(x\) 个节点(下图蓝色的节点),并将这 \(x\) 个点各自与第 2 步放置的节点连上一条无向边。

  4. 对于第 3 步放置的每一个节点,放置 \(y\) 个与其连接的节点(下图中绿色的节点)。

下图是 \(x = 4, y=2\) 的雪花图:

img

给你一棵包含 \(N(3 \le N \le 3 \times 10 ^ 5)\) 个节点的树 \(T\),你需要删除树中的任意的点(也可以一个都不删)以及这些点关联的边。问:最少要删除多少个点,才能使的剩下的图是一颗雪花树?

解题思路

显然是要枚举雪花树红色的点,假设有一个节点 \(u\)\(u\) 的孩子有为 \(v_1, v_2, ..., v_k\),这些孩子对应的度数为 \(d_{v_1}, d_{v_2}, ..., d_{v_k}\),将这些度数排个序,然后枚举可能的 \(y\),如果 \(y\) 很大,则度数小于 \(y\) 的孩子就必须都删掉,度数大于 \(y\) 的点可以只删掉多出来的部分。跑一遍 dfs 枚举就好,不过要注意以下要把孩子的度数都减掉 \(1\),因为 \(u\) 也有 \(1\) 的度数在里面。这样时间复杂度是 \(O(n\log n)\)

参考代码
#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

int main()
{
    int n;
    cin >> n;
    vector<vector<int>> edge(n + 1);
    vector<int> deg(n + 1);
    for (int i = 0; i < n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        deg[u]++;
        deg[v]++;
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    int ans = 0;

    auto dfs = [&edge, &deg, &ans](auto &self, int u, int f) -> void
    {
        vector<int> d = {};
        for (auto v : edge[u])
        {
            if (v != f)
                self(self, v, u);
            d.push_back(deg[v] - 1);
        }
        sort(d.begin(), d.end());
        int len = d.size();
        for (int i = 0; i < len; i++)
            ans = max(ans, 1 + len - i + (len - i) * d[i]);
    };

    dfs(dfs, 1, 0);
    cout << n - ans << endl;
    return 0;
}

F - Visible Buildings

题目大意

\(N(1 \le N \le 2 \times 10 ^ 5)\) 在一条数轴上,其中第 \(i\) 个建筑的横坐标是 \(X_i(1 \le X_1 < X_2 < \cdots < X_N \le 10 ^ 9)\),高度是 \(H_i(1 \le H_i \le 10 ^ 9)\)。某个点的位置可以用横坐标 \(x\) 和纵坐标 \(h\) 来描述。对于点 \(P(x, h)\) 和点 \(Q(x', h')\),如果连线 \(PQ\) 没有与任何建筑相交,则点 \(P\) 能看到点 \(Q\)。你打算站在 \(x=0\) 的某个高度位置 \(h(h > 0)\),问:最高能站在哪个位置,才看不到所有的建筑?如果无论如何都无法避免看到所有建筑,输出 -1

解题思路

因为不能看到所有的建筑,所以至少存在一个建筑 \(j\) 被建筑 \(i(i < j)\) 所阻挡,这可以两两枚算出最大的高度,但是时间复杂度达到 \(O(n^2)\)。还需要推理一些东西,我们考虑最靠后的三个建筑 \(i\)\(j\)\(k\) 的相对高度,固定建筑 \(k\) 的高度,变换建筑 \(i\)\(j\) 的相对高度:

  1. 假设 \(H_j < H_i < H_k\),如下图所示,最大的高度是 \(IJ\) 连线(因为 \(IJ\) 斜率是负数) f1
  2. 假设 \(H_i < H_k\),且 \(H_j\) 高于 \(IK\) 连线,如下图所示,此时 \(IK\) 连线被挡住,那么答案是 \(JK\) 连线 f2
  3. 假设 \(H_i < H_k < H_j\),如下图所示,那么答案是 \(JK\) 连线(因为 \(JK\) 斜率是负数) f3
  4. 假设 \(H_j < H_k < H_i\),如下图所示,那么答案是 \(IJ\) 连线 f4
  5. 假设 \(H_k < H_i\),且 \(H_j\) 高于 \(IK\) 连线,如下图所示,那么答案是 \(JK\) 连线 f5
  6. 假设 \(H_k < H_i < H_j\),如下图所示,那么答案是 \(JK\) 连线(因为 \(JK\) 斜率是负数) f6

以上可知,答案一定是 \(IJ\) 连线或 \(JK\) 连线,而不会有 \(IK\) 连线,所以答案一定是在相邻的两个建筑的连线之间取得。

根据直线的两点式我们有 \(\frac{h-h_1}{x-x_1} = \frac{h_2-h_1}{x_2-x_1}\),代入 \(x=0\) 可得 \(h = -x_1 \times \frac{h_2-h_1}{x_2-x_1} + h_1\)。但是如果直接用这个式子算的话会 WA 两个点,注意到输出的时候精度要求是 \(10^{-9}\),为了减少精度损失,必须化成 \(h = \frac{x_2h_1 - x_1h_2}{x_2-x_1}\),约分之后再转成 double

参考代码
#include <iomanip>
#include <iostream>
#include <numeric>

using namespace std;
using LL = long long;

int main()
{
    int n;
    cin >> n;
    double ans = -1;

    auto cal = [](LL x1, LL h1, LL x2, LL h2) -> double
    {
        LL down = x2 - x1;
        LL up = x2 * h1 - x1 * h2;
        if (up < 0)
            return -1;
        LL g = gcd(down, up);
        down /= g;
        up /= g;
        return up / (double)down;
    };

    LL x1, h1;
    cin >> x1 >> h1;
    while (--n)
    {
        LL x2, h2;
        cin >> x2 >> h2;
        double h = cal(x1, h1, x2, h2);
        ans = max(ans, h);
        h1 = h2;
        x1 = x2;
    }
    cout << setprecision(10) << ans << endl;
    return 0;
}


最后更新: 2025-01-08
创建日期: 2025-01-08