AtCoder Beginner Contest 364 A~F

A.Glutton Takahashi(模拟)

题意:

高桥打算吃 N N N 道菜。
如果 S i = S_i = Si=sweet 是 “甜的”,那么他打算吃的 i i i 道菜就是甜的;如果 S i = S_i = Si=salty 是 “咸的”,那么他打算吃的 i i i 道菜就是咸的。咸的。
如果他连续吃了两道甜菜,他就会感到不适,并且无法再吃任何菜肴。
判断他是否能吃下所有菜肴。

分析:

如果有两个连续sweet出现在字符串中,不包括末尾,那么为Yes,否则为No

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
string s[105];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    while (t--)
    {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            cin >> s[i];
        }
        for (int i = 2; i <= n; i++)
        {
            if (s[i] == "sweet" && s[i - 1] == "sweet" && i != n)
            {
                cout << "No" << endl;
                return 0;
            }
        }
        cout << "Yes" << endl;
    }
    return 0;
}

B.Grid Walk(模拟)

题意:

有一个网格,网格中有 H H H 行和 W W W 列。让 ( i , j ) (i, j) (i,j) 表示从上往下数第 i i i 行和从左往上数第 j j j 列的单元格。

如果 C i , j C_{i, j} Ci,j.,那么单元格 ( i , j ) (i, j) (i,j) 就是空的,如果 C i , j C_{i, j} Ci,j#,那么单元格 ( i , j ) (i, j) (i,j) 就不是空的。

高桥目前位于 ( S i , S j ) (S_i, S_j) (Si,Sj) 单元格,他将按照以下规则依次对 i = 1 , 2 , … , ∣ X ∣ i = 1, 2, \ldots, |X| i=1,2,,X 采取行动。

  • 如果 X X X i i i个字符是 L,并且当前单元格左边的单元格是空的,那么他会移动到左边的单元格。否则,他将留在当前单元格中。
  • 如果 X X X i i i个字符是 R,并且当前单元格右边的单元格是空的,那么他将移动到右边的单元格。否则,他将留在当前的单元格中。
  • 如果 X X X i i i个字符是 U,并且当前单元格的上方存在空格,那么他将移动到上方的单元格。否则,他将留在当前的单元格中。
  • 如果 X X X i i i个字符是 D,并且当前单元格下方存在空格,那么他将移动到下方的单元格。否则,他将停留在当前单元格。

输出他完成一系列操作后所在的单元格。

分析:

按照题目要求模拟即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    while (t--)
    {
        int n, m;
        cin >> n >> m;
        int sx, sy;
        cin >> sx >> sy;
        vector<vector<char>> g(n + 1, vector<char>(m + 1));
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                cin >> g[i][j];
            }
        }
        string op;
        cin >> op;
        for (int i = 0; i < op.length(); i++)
        {
            char x = op[i];
            if (x == 'D')
            {
                if (sx + 1 <= n && g[sx + 1][sy] == '.')
                {
                    sx++;
                }
            }
            else if (x == 'U')
            {
                if (sx - 1 > 0 && g[sx - 1][sy] == '.')
                {
                    sx--;
                }
            }
            else if (x == 'R')
            {
                if (sy + 1 <= m && g[sx][sy + 1] == '.')
                {
                    sy++;
                }
            }
            else if (x == 'L')
            {
                if (sy - 1 > 0 && g[sx][sy - 1] == '.')
                {
                    sy--;
                }
            }
        }
        cout << sx << ' ' << sy << endl;
    }
    return 0;
}

C.Minimum Glutton(贪心)

题意:

N N N 道菜,其中 i i i 道菜的甜度为 A i A_i Ai ,咸度为 B i B_i Bi

高桥打算将这些 N N N 菜肴按照自己喜欢的顺序排列,然后按照这个顺序吃掉。

他将按照排列顺序吃掉这些菜肴,但是一旦他吃掉的菜肴的总甜度超过 X X X 或总咸度超过 Y Y Y ,他就会停止进食。

求他最后吃掉的菜肴的最少数量。

分析:

为了让更早结束,我们显然只需要考虑其中一种属性。将甜度与咸度分离,分别排序,取符合条件的最小值即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
LL a[maxn], b[maxn];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    while (t--)
    {
        LL n, x, y;
        cin >> n >> x >> y;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        for (int i = 1; i <= n; i++)
            cin >> b[i];
        sort(a + 1, a + 1 + n);
        sort(b + 1, b + 1 + n);
        LL ans = n;
        LL tmp = 0;
        for (int i = n; i >= 1; i--)
        {
            tmp += a[i];
            ans = i;
            if (tmp > x)
                break;
        }
        tmp = 0;
        LL ans1 = 0;
        for (int i = n; i >= 1; i--)
        {
            tmp += b[i];
            ans1 = i;
            if (tmp > y)
                break;
        }
        cout << n - max(ans, ans1) + 1 << endl;
    }
    return 0;
}

D.K-th Nearest (二分)

题意:

在一条数线上有 N + Q N+Q N+Q 个点 A 1 , … , A N , B 1 , … , B Q A_1,\dots,A_N,B_1,\dots,B_Q A1,,AN,B1,,BQ ,其中点 A i A_i Ai 的坐标为 a i a_i ai ,点 B j B_j Bj 的坐标为 b j b_j bj

就每个 j = 1 , 2 , … , Q j=1,2,\dots,Q j=1,2,,Q 回答下面的问题:

  • X X X A 1 , A 2 , … , A N A_1,A_2,\dots,A_N A1,A2,,AN 中最靠近点 B j B_j Bj k j k_j kj点。求点 X X X B j B_j Bj 之间的距离。更具体地说,设 d i d_i di 是点 A i A_i Ai B j B_j Bj 之间的距离。将 ( d 1 , d 2 , … , d N ) (d_1,d_2,\dots,d_N) (d1,d2,,dN) 按升序排序,得到序列 ( d 1 ′ , d 2 ′ , … , d N ′ ) (d_1',d_2',\dots,d_N') (d1,d2,,dN) 。求 d k j ′ d_{k_j}' dkj .

分析:

我们先将 a i a_i ai排序,对于每一个 b i b_i bi,我们可以二分这个距离是 x x x,然后看 [ t m p − m , t m p + m ] [tmp−m , tmp+m] [tmpm,tmp+m]的点的数量,如果是大于等于 k k k,则说明该距离是第 k k k小或更大距离,则往小的方向收缩。而统计点的数量则通过两次二分点坐标即可

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
int a[maxn], b[maxn], k[maxn];
int tmp, num, n, q;
bool check(int x)
{
    int idxhigh = upper_bound(a + 1, a + 1 + n, tmp + x) - a;
    int idxlow = lower_bound(a + 1, a + 1 + n, tmp - x) - a;
    idxhigh--;
    int res = idxhigh - idxlow + 1;
    return res >= num;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    while (t--)
    {
        cin >> n >> q;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        for (int i = 1; i <= q; i++)
            cin >> b[i] >> k[i];
        sort(a + 1, a + 1 + n);
        for (int i = 1; i <= q; i++)
        {

            tmp = b[i];
            num = k[i];
            int low = 0, high = 2e8;
            while (low < high)
            {
                int mid = low + high >> 1;
                if (check(mid))
                {
                    high = mid;
                }
                else
                    low = mid + 1;
            }
            cout << high << endl;
        }
    }
    return 0;
}

E.Maximum Glutton (dp)

题意:

高桥为斯努克准备了 N N N 道菜。菜肴的编号从 1 1 1 N N N ,菜肴 i i i 的甜度为 A i A_i Ai ,咸度为 B i B_i Bi
高桥可以按照自己喜欢的顺序排列这些菜肴。斯努克会按照排列顺序吃掉这些菜肴,但如果他吃过的菜肴的总甜度超过 X X X 或总咸度超过 Y Y Y ,他就不会再吃任何菜肴。
高桥希望斯努克吃尽可能多的菜肴。求如果高桥把菜肴摆放得最合理,斯努克最多吃的菜肴数。

分析:

d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i个菜品,甜度为 j j j时的最小咸度,有转移方程为: d p [ j ] [ k ] = m i n ( d p [ j ] [ k ] , d p [ j − 1 ] [ k − a ] + b ) ; dp[j][k] = min(dp[j][k], dp[j - 1][k - a] + b); dp[j][k]=min(dp[j][k],dp[j1][ka]+b);。最后只要求得 d p [ i ] [ j ] ≤ y dp[i][j] \le y dp[i][j]y的最大的 i i i即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
const int N = 1e2 + 10, M = 1e4 + 10;
int dp[N][M], n, a, b, x, y;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    while (t--)
    {
        cin >> 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++)
        {
            cin >> 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++)
            {
                if (dp[i][j] <= y)
                {
                    cout << i + 1 << endl;
                    return 0;
                }
            }
        }
    }
    cout << 1 << endl;
    return 0;
}

F.Range Connect MST (图论)

题意:

有一个图,它有 N + Q N + Q N+Q 个顶点,编号为 1 , 2 , … , N + Q 1, 2, \ldots, N + Q 1,2,,N+Q 。最初,该图没有边。
对于这个图,请依次对 i = 1 , 2 , … , Q i = 1, 2, \ldots, Q i=1,2,,Q 执行以下操作:

  • 对于每个满足 L i ≤ j ≤ R i L_i \leq j \leq R_i LijRi 的整数 j j j ,在顶点 N + i N + i N+i j j j 之间添加一条代价为 C i C_i Ci 的无向边。

完成所有操作后,确定图形是否相连。如果相连,求该图的最小生成树的代价。

分析:

考虑是否构成连通块,即这 q q q个线段是否构成一个大线段。再考虑最小生成树怎么求,即考虑前 n n n个点该和哪个操作点 ( n + i ) (n+i) (n+i)连边。
我们从代价小的操作开始,给 L i ≤ j ≤ R i L_i \le j \le R_i LijRi 中的每个点合并成一个联通块,每合并一次的代价是 C i C_i Ci。最后看是否是同个连通块即可。
连通块利用并查集维护,合并时总是以编号大的点为根,这样在上述从左到右合并时每次都从还未连通的点开始遍历即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int maxn = 2e5 + 5;
const int INF = 1e9;
const int mod = 1e9 + 7;
int fa[maxn], rmax[maxn];
int n, q;
struct node
{
    int l, r, c;
} a[maxn];
bool cmp(node p, node q)
{
    return p.c < q.c;
}
LL ans;
int find(int x)
{
    if (fa[x] == x)
        return x;
    return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
    int fx = find(x), fy = find(y);
    if (fx != fy)
    {
        fa[fx] = fy;
        rmax[fy] = max(rmax[fx], rmax[fy]);
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    while (t--)
    {
        cin >> n >> q;
        for (int i = 1; i <= n; ++i)
            fa[i] = rmax[i] = i;
        for (int i = 1; i <= q; ++i)
        {
            cin >> a[i].l >> a[i].r >> a[i].c;
        }
        sort(a + 1, a + 1 + q, cmp);
        for (int i = 1; i <= q; ++i)
        {
            int now = a[i].l;
            while (1)
            {
                ans += 1ll * a[i].c;
                int nxt = rmax[find(now)] + 1;
                if (nxt > a[i].r)
                    break;
                else
                {
                    merge(now, nxt);
                    now = nxt;
                }
            }
        }
        if (rmax[find(1)] != n)
            cout << -1 << endl;
        else
            cout << ans << endl;
    }
    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值