AtCoder Beginner Contest 364 Review

-1. 前言

我!进!前!250!了!!!

0. 沉浸体验

20:00 噫!好!我网没卡!(被 \(7\)\(23\) 日的 CF 整到 PTSD 的我的反应)

A. Glutton Takahashi 这啥玩意

哦!直接上 map 判断 sweet 的数量是不是大于 \(\lceil\frac{n}{2}\rceil\) 就行了!

打完 Winter Camp! 没过样例!

再看一眼题目 Winter Camp! 不能改变顺序!

删掉代码重新打完 好!样例过了!交!

20:02:56 过。

B. Grid Walk 布什戈门,第二题就让我打模拟是吧

好!打!

打完 好!样例过了!交!

20:06:31 过。

C. Minimum Glutton 怎么 Glutton 又出现了

诈骗是吧,故意让我们把 \(A_i\)\(B_i\) 绑在一起然后不说这两东西可以分离

疑?悟!

打完 啊?没过样例?

乱改参数 啊?还是没过?

静态查错 Winter Camp! \(n-i+1\) 打成 \(i\) 了!

改过来 好!过样例了!交!

21:12:25 过。

D. K-th Nearest 啊?

布什戈门,怎么 D 就上序列维护问题啊

仔细再看一次题目 Winter Camp! 理解错题目了!直接将 \(a\) 排序然后二分答案用 STL 辅助 check 就可以了!我是 Subtask!

打完 啊?怎么没过样例?

仔细看一眼样例 我代码也没问题啊?

再仔细看一眼样例 Winter Camp! 二分区间右端点设小了!每次都要设成 \(2\times 10^8\)!我是 Subtask!

改完代码 好!过样例了!交!

20:19:08 过。

E. Maximum GluttonGlutton 都出现三次了是不是图谋不轨啊

啊?二重 Meet-in-Middle?啊?(此时没想到“那个”解法)

不会,换一题。

F. Range Connect MST MST,最小生成树,想起了一些不好的过往

啊?线段树辅助建图上最小生成树?啊?

不会,换一题。

G. Last Major City 这……莫名有种宿命感

啊?我记得我好像做过类似的题?啊?

忘了怎么写了啊啊啊啊啊啊啊啊啊

回去想 F

冷静,这可是 AtCoder,不会有大码量毒瘤题的

嗯?没有大码量?也就是说没有线段树?

想起了之前自己出的差分题 啊啊啊啊,这题果然不用线段树!

直接标记每条边所属的节点编号,边权,以及其出现和消失的位置,然后加一个并查集维护信息就可以了!我是 Subtask!

打完 啊?运行错误?

输出变量 啊?问题出在启发式合并环节?

静态查错 Winter Camp! 我少打了一个字母!

改好 好!过样例了!交!

20:55:02 过!没想到跑得这么快!

回去看 E 还是不行啊,\(nxy\) 铁定会超的

突然回想起之前学弟出的一道题 尤里卡!倒反天罡!

其实我们不用拘泥于求出每个总甜/咸度值的可行性,而是求出在吃掉 \(i\) 个菜品且甜度为 \(j\) 时最小的咸度值 \(d_{i,j}\)

然后,时间复杂度就成功地降至了 \(O(n^2x)\)!这下肯定能过!

打完 啊?样例错了?

突然想到输出的答案要加一 然后过了两个样例。

那还是有问题啊。

静态查错 Winter Camp! 打错了两个字符!

改完 好!过样例了!交!

21:12:01 过!用时竟然这么少!

然后后面就是摆烂了。G 不会一点。

下面进入正题。

1. Glutton Takahashi

模拟即可。

显然,当两个 sweet 同时出现在末尾时,答案为 Yes,否则是 No

#include <iostream>
#include <string>
#include <map>
using namespace std;
const int N = 1e5 + 10;
int n;
string s, ps;
map<string, int> mp;
bool fl;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> s;
        if (fl)
            return puts("No"), 0;
        if (s == "sweet" and ps == "sweet")
            fl = true;
        ps = s;
    }
    puts("Yes");
}

2. Grid Walk

模拟。

题面过于详细,直接按照题目实现即可。

#include <iostream>
#include <string>
#include <map>
using namespace std;
const int N = 5e2 + 10;
int n, m, x, y, nx, ny;
char mp[N][N];
map<char, pair<int, int>> tmp;
string s;
int main()
{
    cin >> n >> m >> x >> y;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> mp[i][j];
        }
    }
    cin >> s;
    tmp['L'] = {0, -1};
    tmp['R'] = {0, 1};
    tmp['U'] = {-1, 0};
    tmp['D'] = {1, 0};
    for (auto &i : s)
    {
        auto &[fx, fy] = tmp[i];
        nx = x + fx, ny = y + fy;
        if (nx < 1 or nx > n or ny < 1 or ny > m or mp[nx][ny] == '#')
            continue;
        x = nx, y = ny;
    }
    printf("%d %d\n", x, y);
}

3. Minimum Glutton

为了让 Takahashi 更早结束,我们显然只需要考虑其中一种属性。

将甜度与咸度分离,分别排序,取符合条件的最小值即可。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
int n;
using ll = long long;
ll x, y;
int a[N], b[N], res;
int main()
{
    scanf("%d%lld%lld", &n, &x, &y);
    res = n;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", a + i);
    }
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", b + i);
    }
    sort(a + 1, a + n + 1);
    sort(b + 1, b + n + 1);
    for (int i = n; i; i--)
    {
        x -= a[i];
        if (x < 0)
        {
            res = min(res, n - i + 1);
            break;
        }
    }
    for (int i = n; i; i--)
    {
        y -= b[i];
        if (y < 0)
        {
            res = min(res, n - i + 1);
            break;
        }
    }
    printf("%d\n", res);
}

4. K-th Nearest

一维数轴上 \(k\) 小距离问题。

\(a\) 排序后借助 STL 进行二分,求得符合各个距离限度的点数量,进行二分逼近即可。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, q, a[N], b, k, l, r, mid, tr = 2e8;
int main()
{
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", a + i);
    }
    sort(a + 1, a + n + 1);
    // tr = a[n] - a[1];
    for (int i = 1; i <= q; i++)
    {
        scanf("%d%d", &b, &k);
        l = 0, r = tr;
        while (l < r)
        {
            mid = (l + r) >> 1;
            if (upper_bound(a + 1, a + n + 1, b + mid) - lower_bound(a + 1, a + n + 1, b - mid) < k)
                l = mid + 1;
            else
                r = mid;
        }
        printf("%d\n", l);
    }
}

5. Maximum Glutton

最直观的 DP 方法是设 \(d_{i,j}\) 表示甜度为 \(i\) 且咸度为 \(j\) 的可行性。

但是,这样的时间复杂度是 \(O(nxy)\),显然是不可接受的。

为了解决问题,我们需要进行一个倒反天罡。

\(d_{i,j}\) 表示 \(i\) 个菜品甜度为 \(j\) 时的最小咸度。这样设计的好处是,将单次转移所用的时间复杂度从 \(O(xy)\) 降至 \(O(nx)\)。最终求可行性时,只需要满足 \(d_{i,j}\le y\) 就可以了。

时间复杂度 \(O(n^2x)\)

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e2 + 10, M = 1e4 + 10;
int dp[N][M], n, a, b, x, y;
int main()
{
    scanf("%d%d%d", &n, &x, &y);
    memset(dp, 0x3f, sizeof dp);
    for (int i = 0; i <= x; i++)
        dp[0][i] = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d", &a, &b);
        for (int j = i; j; j--)
        {
            for (int k = x; k >= a; k--)
            {
                dp[j][k] = min(dp[j][k], dp[j - 1][k - a] + b);
            }
        }
    }
    for (int i = n - 1; i; i--)
    {
        for (int j = 0; j <= x; j++)
        {
            // printf("%d %d %d\n", i, j, dp[i][j]);
            if (dp[i][j] <= y)
                return printf("%d\n", i + 1), 0;
        }
    }
    puts("1");
}

6. Range Connect MST

根据题目可以得出,从每个点连出的边的边权是完全一致的。

换句话说,我们可以对在同一连通块内的所有节点只选一条最短的边连接。信息可以借助并查集、map 以及 multiset 进行合并。

可以借助差分的思想,在区间左端点插入,在区间右端点删除。这里的区间是左闭右开的。

时间复杂度 \(O((n+q)\log n)\)

#include <iostream>
#include <map>
#include <set>
#include <vector>
using namespace std;
const int N = 4e5 + 10;
int n, f[N], siz[N], q, l, r, c;
inline int find(int x)
{
    return x == f[x] ? x : f[x] = find(f[x]);
}
inline void merge(int x, int y)
{
    x = find(x), y = find(y);
    if (x == y)
        return;
    f[y] = x;
    siz[x] += siz[y];
    siz[y] = 0;
}
using pii = pair<int, int>;
vector<pii> vs[N], er[N];
map<int, multiset<int>> mp, tmp;
using ll = long long;
ll res;
int main()
{
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= q; i++)
    {
        scanf("%d%d%d", &l, &r, &c);
        vs[l].push_back({n + i, c});
        er[r + 1].push_back({n + i, c});
    }
    for (int i = 1; i <= n + q; i++)
        f[i] = i, siz[i] = 1;
    for (int i = 1; i <= n; i++)
    {
        for (auto [j, tv] : er[i])
        {
            mp[find(j)].erase(mp[find(j)].find(tv));
        }
        tmp.clear();
        // printf("%d\n", mp.size());
        for (auto &[j, tv] : vs[i])
        {
            mp[find(j)].insert(tv);
        }
        for (auto &[k, v] : mp)
        {
            if (!v.size())
                continue;
            merge(k, i);
            res += *v.begin();
        }
        for (auto &[k, v] : mp)
        {
            if (!v.size())
                continue;
            if (v.size() > tmp[find(k)].size())
            {
                tmp[find(k)].swap(v);
            }
            for (auto &j : v)
                tmp[find(k)].insert(j);
        }
        mp.swap(tmp);
    }
    if (siz[find(1)] != n + q)
        return puts("-1"), 0;
    printf("%lld\n", res);
}
posted @ 2024-07-27 23:51  丝羽绫华  阅读(160)  评论(0)    收藏  举报