首页 > 其他分享 >AtCoder Beginner Contest 370

AtCoder Beginner Contest 370

时间:2024-09-07 22:26:00浏览次数:5  
标签:AtCoder Beginner int sum pos erase 370 hset dp

A - Raise Both Hands (abc370 A)

题目大意

给出Snuke举的左右手情况,如果只举左手,输出Yes,如果只举右手,输出No,否则输出Invalid

解题思路

逐一判断即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int l, r;
    cin >> l >> r;
    if (l == 1 && r == 0)
        cout << "Yes" << '\n';
    else if (l == 0 && r == 1)
        cout << "No" << '\n';
    else
        cout << "Invalid" << '\n';

    return 0;
}



B - Binary Alchemy (abc370 B)

题目大意

给定物品合成成分表\(a_{ij}\)表示物品 \(i\)和物品 \(j\)合成物品 \(a_{ij}\)。

问物品 \(1\),依次与 \(1,2,3,..N\)物品合成,问最后的物品。

解题思路

按照题意查表,模拟合成即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<vector<int>> a(n);
    for (int i = 0; i < n; i++) {
        a[i].resize(i + 1);
        for (auto& x : a[i]) {
            cin >> x;
            --x;
        }
    }
    int cur = 0;
    for (int i = 0; i < n; ++i) {
        int x = cur, y = i;
        if (x < y)
            swap(x, y);
        cur = a[x][y];
    }
    cout << cur + 1 << '\n';

    return 0;
}



C - Word Ladder (abc370 C)

题目大意

给定两个字符串\(s,t\)。

用最小的次数,使得 \(s=t\),并且字符串\(x\)的字典序最小。

操作为,选择 \(s_i = c\),并且将修改后的 \(s\)放入 \(x\)的末尾。

解题思路

如何次数最小呢?

依次考虑\(s\)从左到右的每一位 \(i\),如果 \(s_i \neq t_i\),那我肯定要 \(s_i = t_i\),但这是我们此时要进行的操作吗?还是先放一放,改后面的字母后,再改当前位?

由于每次会将修改后的\(s\)放入 \(x\)的末尾,因此我们要优先考虑首先进行的操作,应该是:即刻进行,还是缓一缓在进行。

如果\(s_i > t_i\),那就优先更改当前位,这样改后的 \(s\)的字典序更小。

如果 \(s_i < t_i\),那就先更改后面位的,最后再改当前位,这样得到的 \(x\)的字典序最小。

这种回溯的感觉,可以用\(DFS\)实现上述操作。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    string s, t;
    cin >> s >> t;
    vector<string> ans;
    auto dfs = [&](auto dfs, int pos) {
        if (pos == s.size()) {
            return;
        }
        if (s[pos] == t[pos]) {
            dfs(dfs, pos + 1);
        } else if (s[pos] < t[pos]) {
            dfs(dfs, pos + 1);
            s[pos] = t[pos];
            ans.push_back(s);
        } else {
            s[pos] = t[pos];
            ans.push_back(s);
            dfs(dfs, pos + 1);
        }
    };
    dfs(dfs, 0);
    cout << ans.size() << '\n';
    for (auto& i : ans)
        cout << i << '\n';

    return 0;
}



D - Cross Explosion (abc370 D)

题目大意

二维网格,初始每个格子有墙。

依次进行\(q\)次放炸弹的操作,给定每次放炸弹的位置 \((i,j)\),如果该位置有墙,则该墙消失。

否则,炸弹会爆炸,会产生十字冲击波,该位置上下左右的各第一个墙都会消失。

问最后还存在的墙的数量。

解题思路

对于第一种情况,直接移除该位置的墙即可。

对于第二种情况,需要找到该列上下、该行左右最近的墙。

墙的数量\(hw \leq 4e5\),可以储存每个墙的坐标。

然后对于每行和每列,分别维护\(hset[i]\)表示第\(i\)行还有墙的列坐标,是个\(set\),\(wset[i]\)表示第 \(i\)列还有墙的行坐标 ,也是个\(set\)。

这样,对于一个炸弹 \((i,j)\),如果该位置没有墙\((hset[i].find(j) == hset[i].end())\),则需要找到 \(< j\)的最大和 \(> j\) 的最小的数字。同理对于\(wset\)也要找对应的数字,然后 \(erase\)。这样每次操作的复杂度都是 \(O(\log)\),总的时间复杂度就是 \(O(q\log (h + w))\)。

代码对于没有墙的逻辑是:

  • \(it = hset[i].lower\_bound(j)\), 由于没有墙,此时一定 \(*it > y\)(否则是 \(*it == y\)),如果 \(it != hset[i].end()\),那么它就是下面的第一个墙(这里认为左上是原点),要毁掉,于是\(it = hset[i].erase(it)\), \(erase\)返回值是移除了该 \(*it\)后的下一个元素。
  • 然后要找上面的第一个墙,此时 \(it\)是 \(>y\)的第一个位置(无论刚刚是否\(erase\)了),因此如果 \(it != hset[i].begin()\),那么 \(prev(it)\)就是上面的第一个墙,要毁掉,于是 \(hset[i].erase(prev(it))\)。

同理的思路处理 \(wset\)即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int h, w, q;
    cin >> h >> w >> q;
    vector<int> hh(h), ww(w);
    iota(hh.begin(), hh.end(), 0);
    iota(ww.begin(), ww.end(), 0);
    vector<set<int>> hset(h), wset(w);
    for (int i = 0; i < h; i++) {
        hset[i].insert(ww.begin(), ww.end());
    }
    for (int i = 0; i < w; i++) {
        wset[i].insert(hh.begin(), hh.end());
    }
    while (q--) {
        int x, y;
        cin >> x >> y;
        --x, --y;
        auto it = hset[x].lower_bound(y);
        if (it != hset[x].end() && *it == y) {
            hset[x].erase(y);
            wset[y].erase(x);
        } else {
            if (it != hset[x].end()) {
                wset[*it].erase(x);
                it = hset[x].erase(it);
            }
            if (it != hset[x].begin()) {
                it = prev(it);
                wset[*it].erase(x);
                hset[x].erase(it);
            }
            it = wset[y].lower_bound(x);
            if (it != wset[y].end()) {
                hset[*it].erase(y);
                it = wset[y].erase(it);
            }
            if (it != wset[y].begin()) {
                it = prev(it);
                hset[*it].erase(y);
                wset[y].erase(it);
            }
        }
    }
    int cnt = 0;
    for (int i = 0; i < h; i++) {
        cnt += hset[i].size();
    }
    cout << cnt << '\n';

    return 0;
}



E - Avoid K Partition (abc370 E)

题目大意

给定一个数组\(a\),划分成若干个子区间,使得没有子区间的和为 \(k\)。

求划分方案数。

解题思路

朴素\(dp\)就是设 \(dp[i]\)表示前 \(i\)段划分满足条件的方案数。

转移则枚举最后一次的区间,然后 \(dp[i] = \sum_{1 \leq j \leq n, sum[j+1..i] \neq k} dp[j]\)。

复杂度显然是 \(O(n^2)\)的。

棘手在条件 \(sum[j+1..i] \neq k\)上,如果没有这个条件,这个转移其实就是一个前缀和,用前缀和优化即为 \(O(n)\)。

我们用前缀和相减代替区间和,即 \(sum[j+1..i] = sum[i] - sum[j]\),转移式即为\(dp[i] = \sum_{1 \leq j \leq n, sum[i] - sum[j] = k} dp[j]\)。

换句话说,我们要对\(sum[j] \neq sum[i] - k\)的 \(dp[j]\)求和,这是个非常稀疏的条件,即如果设 \(cnt[i] = \sum_{sum[j] = i} dp[j]\),即前缀和为 \(i\)的 \(dp\)值,那上述转移式可改写成\(dp[i] = \sum_{1 \leq j < i} dp[j] - cnt[sum[i] - k]\)。

即一个前缀和与一个数的差值,这样转移就是\(O(1)\)了,因此维护一个\(dp\)前缀和 \(\sum_{1 \leq j < i} dp[j]\)以及前缀和的\(dp\)和\(cnt[i] = \sum_{sum[j] = i} dp[j]\)即可。

时间复杂度就是\(O(n \log n)\)。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int mo = 998244353;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    LL k;
    cin >> n >> k;
    vector<int> a(n);
    for (auto& x : a)
        cin >> x;
    map<LL, int> cnt;
    cnt[0] = 1;
    LL presum = 0;
    int precnt = 1;
    int ans = 0;
    for (auto& i : a) {
        presum += i;
        ans = (precnt - cnt[presum - k] + mo) % mo;
        cnt[presum] = (cnt[presum] + ans) % mo;
        precnt = (precnt + ans) % mo;
    };
    cout << ans << '\n';

    return 0;
}



F - Cake Division (abc370 F)

题目大意

给定一个环形数组,划分为\(k\)段,使得每段和的最小值最大。

在该最大值的各种划分方案中,求有多少位置,在所有划分方案中都不被分开。

解题思路

<++>

神奇的代码



G - Divisible by 3 (abc370 G)

题目大意

如果一个数是好的,说明它的因子和能被\(3\)整除。

给定 \(n,m\),问一个长度为 \(m\)的数组 \(a\)的数量,满足其各数的乘积不超过 \(n\),且是好数。

解题思路

<++>

神奇的代码



标签:AtCoder,Beginner,int,sum,pos,erase,370,hset,dp
From: https://www.cnblogs.com/Lanly/p/18402239

相关文章

  • abc370 A-E题解 (AtCoder Beginner Contest 370)
     A这类简单题,看清楚:OutputPrint Yes, No,or Invalid accordingtotheinstructionsintheproblemstatement. B Cstd,这样写(0->n-1,n-1->0),可以少写一个vector1#include<bits/stdc++.h>2usingnamespacestd;34intmain(){5strings,......
  • 题解:Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
    总体情况这次手速够快:ABCin10min,ABCDEin30min。A-RaiseBothHands思路分类讨论很简单的。注意一定要判断\(0,0\)这种情况。Code//Problem:A-RaiseBothHands//Contest:AtCoder-ToyotaProgrammingContest2024#9(AtCoderBeginnerContest370)//URL:......
  • ABC370掉分寄
    在数月不打ABC后,我为了找回打比赛的手感开始打ABC,结果挂得不堪入目。开场前15min,在360的可爱操作下我的AtcoderBetter被卸了,我就重装,然后在油叉里一不小心点了两遍,重装了俩,一登界面发现两个还一块使,导致交替的时候完全找不到提交按钮,极速换浏览器,可是看不了中文题面了。......
  • AtCoder Beginner Contest 369
    A-369(abc369A)题目大意给定两个数\(a,b\),问有多少个整数\(x\),使得\(a,b,x\)经过某种排列后成为等差数列,解题思路就三种情况:\(xab\),\(axb\),\(abx\),三种情况都求出来,然后放到set去重即为答案。中间的情况要判断是否是实数。神奇的代码#include<bits/stdc++.h>using......
  • AtCoder Beginner Contest 369 题ABCD详细题解--包含解题思路和两种语言(C++,Python)
    前言:    本文为AtCoderBeginnerContest369题ABCD详细题解,包括题目大意,详细的解题思路和两种语言描述,觉的有帮助或者写的不错可以点个赞几天前的比赛拖的有点久了比赛题目连接:Tasks-AtCoderBeginnerContest369目录题A:题目大意和解题思路:代码(C++):......
  • AtCoder ABC 369题解
    前言本题解部分思路来源于网络,仅供参考!A-369题目大意给定\(A\),\(B\)两个整数,求有多少个整数\(x\)使得可以通过某种排列使得\(A\),\(B\),\(x\)为等差数列。解题思路稍加分析即可得到:如果\(A=B\)则结果为\(1\)。如果\(A=B\)但\((A+B)\bmod......
  • ADM1370 Applications of Information Technology for Business
    ADM1370Applicationsof InformationTechnologyfor BusinessAssignment3–MicrosoftAccessDatabase:acollectionof data, orinformation, thatisspecially organized forrapid searchandretrievalbyacomputer.Databases arestructured to facil......
  • AtCoder Beginner Contest 369 补题记录
    A-369题意:给定A和B,求有多少个x可以和A,B构成等差数列思路:分三种情况讨论A==B则x不得不与A和B想等x位于A和B中间只有B-A为偶数才有这种情况存在x位于A和B两边可以在左边也可以在右边,只要A!=B这种情况总会存在voidsolve(){inta=read(),b=read();......
  • AtCoder Beginner Contest 368(ABC368)
    [ABC369C]CountArithmeticSubarrays题意:判断有多少个区间是等差数列(不能重排)。\(1\len\times10^5\)。思路:赛时看错题了,以为这个区间可以重排,卡了8min,小丑了。首先容易注意到,对于一个区间\([l,r]\),若其是等差数列,则这个区间的子区间\([l',r']\)肯定也是子序列,造成......
  • Atcoder Beginner Contest 369
    AtcoderBeginnerContest369C-CountArithmeticSubarrays题意给出一个长度为\(n\)的序列\(A\),求有多少个\(A\)的连续子序列为等差数列。思路对于递增的右端点,左端点不减。使用双指针,枚举右端点,扫描左端点。时间复杂度:\(O(n)\)。代码#include<bits/stdc++.h>usi......