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

AtCoder Beginner Contest 372

时间:2024-09-21 22:24:31浏览次数:7  
标签:AtCoder return Beginner int tr cin dfs vector 372

省流版
  • A. 暴力即可
  • B. 转换3进制即可
  • C. 考虑答案的组成,仅修改发生变化的部分即可
  • D. 维护答案数组\(ans_i\),考虑枚举 \(j\)对哪些 \(i\)有贡献,通过单调栈找到对应的区间\(i\) ,通过差分维护区间加法即可
  • E. 并查集维护连通块,\(set\)维护点标号大小,合并\(set\)时启发式合并,查询则从对应连通块的\(set\)最大值往前找\(k\)次即可
  • F. 考虑朴素\(dp\),发现有效转移只有 \(O(mk)\) 个,记忆化搜索即可

A - delete . (abc372 A)

题目大意

给定一个字符串,删除其中的.

解题思路

依次判断每个字符,如果不是.就输出。

python可以一行,

神奇的代码
print(input().replace('.', ''))


B - 3^A (abc372 B)

题目大意

给定一个\(m\),将其表达成若干个 \(3\)的幂的和。

解题思路

其实就是将\(m\)转换成三进制,看三进制下每一个幂的系数。

进制转换一下即可。

神奇的代码
#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 m;
    cin >> m;
    vector<int> ans;
    for (int i = 0; i <= 10; ++i) {
        int cnt = m % 3;
        while (cnt--) {
            ans.push_back(i);
        }
        m /= 3;
    }
    cout << ans.size() << '\n';
    for (auto i : ans)
        cout << i << " ";
    cout << '\n';

    return 0;
}



C - Count ABC Again (abc372 C)

题目大意

给定一个字符串\(s\),进行以下\(q\)次操作。

每次操作,将\(s_x = c\)。问操作完后,字符串 ABC在\(s\)的出现次数。

解题思路

记\(f(i) = 1/0\)表示 \(s_is_{i+1}s_{i+2}\)是/不是 ABC

答案就是\(\sum_{i = 1}^{n} f(i)\)。

每次修改,其实只有 \(3\)个 \(f(i)\)的值可能发生变化。

因此先计算出 \(\sum_{i = 1}^{n} f(i)\),然后对于每次修改,先减去\(f(x-2)+f(x-1)+f(x)\)个 ,然后修改字符后,重新计算\(f(x-2)+f(x-1)+f(x)\)即可得到新的答案,而不需要重新计算 \(\sum_{i = 1}^{n} f(i)\)。

时间复杂度就为\(O(q)\)。

神奇的代码
#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, q;
    string s;
    cin >> n >> q >> s;
    int ans = 0;
    auto in_range = [&](int i) { return i >= 0 && i < n; };
    auto check = [&](int x) {
        for (int i = 0; i < 3; ++i)
            if (!in_range(x + i) || s[x + i] != 'A' + i)
                return false;
        return true;
    };
    for (int i = 0; i < n; ++i) {
        ans += check(i);
    }
    while (q--) {
        int x;
        string c;
        cin >> x >> c;
        --x;
        for (int i = x - 2; i <= x; ++i) {
            ans -= check(i);
        }
        s[x] = c[0];
        for (int i = x - 2; i <= x; ++i) {
            ans += check(i);
        }
        cout << ans << '\n';
    }

    return 0;
}



D - Buildings (abc372 D)

题目大意

给定\(n\)个数的数组 \(a\),对于每个 \(i \in [1,n]\) ,问\(j\)的数量满足 \((i,j]\)中 \(a_j\)是最大值。

解题思路

如果枚举\(i\),求 \(j\),复杂度是 \(O(n^2)\)。

反过来,枚举 \(j\),考虑它会对哪些 \(i\)有 \(1\)的贡献。 会发现只要满足\(\max(a[i+1..j]) \leq j\),那么这个 \(j\)就会对 \(i\)有 \(1\)的贡献。

即对于每个数\(a_j\),我们找其左边第一个\(a_l > a_j\),那么\(ans[l..j-1] += 1\)

对于第一个,如何找到其左边第一个\(a_l > a_j\)呢?这是一个经典问题,从右往左,用单调栈维护一个递减的数列即可(栈顶到栈底是递减的)。

对于第二个,有若干次区间加法,但最后只有一次查询,可以用差分数组将区间加变成单点修改,最后还原出答案数组即可。

神奇的代码
#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<int> h(n);
    for (auto& x : h)
        cin >> x;
    stack<int> s;
    vector<int> diff(n + 1);
    for (int i = n - 1; i >= 0; i--) {
        while (!s.empty() && h[s.top()] < h[i]) {
            diff[i]++;
            diff[s.top()]--;
            s.pop();
        }
        s.push(i);
    }
    while (!s.empty()) {
        diff[0]++;
        diff[s.top()]--;
        s.pop();
    }
    int ans = 0;
    for (int i = 0; i < n; ++i) {
        ans += diff[i];
        cout << ans << " \n"[i == n - 1];
    }

    return 0;
}



E - K-th Largest Connected Components (abc372 E)

题目大意

给定\(n\)个点,初始无边。维护两类操作。

  • 1 u v,连一条无向边\(u \to v\)
  • 2 v k,找\(v\)所在连通块的第 \(k\)大的点的标号。

\(k \leq 10\)。

解题思路

注意到\(k \leq 10\),如果能用 \(set\)维护一个连通块的所有点的标号,那么直接从 rbegin()暴力数\(k\)个就是答案,

如果维护这个 \(set\)呢?连通块的信息自然用并查集维护,当合并两个连通块时,其 \(set\)也要合并,我们可以将小的 \(set\)合并到大的 \(set\)中,即启发式合并,每次合并 \(set\)最坏的复杂度是 \(O(n)\),但最坏情况下,合并后的 \(set\)大小会翻倍,最多翻倍 \(log\)次就达到 \(n\)个点,即最坏情况的出现次数只有 \(O(\log n)\)次,因此启发式合并的时间复杂度是 \(O(n \log n)\)。

维护好 \(set\)后,直接从 \(rbegin()\)数 \(k\)个点即为答案。特判少于\(k\)个点的情况。

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

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

class dsu {
  public:
    vector<int> p;
    vector<int> sz;
    vector<set<int>> node;
    int n;

    dsu(int _n) : n(_n) {
        p.resize(n);
        sz.resize(n);
        node.resize(n);
        iota(p.begin(), p.end(), 0);
        fill(sz.begin(), sz.end(), 1);
        for (int i = 0; i < n; i++) {
            node[i].insert(i);
        }
    }

    inline int get(int x) { return (x == p[x] ? x : (p[x] = get(p[x]))); }

    inline bool unite(int x, int y) {
        x = get(x);
        y = get(y);
        if (x != y) {
            if (sz[x] < sz[y])
                swap(x, y);
            node[x].insert(node[y].begin(), node[y].end());
            p[y] = x;
            sz[x] += sz[y];
            return true;
        }
        return false;
    }
};

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, q;
    cin >> n >> q;
    dsu d(n);
    while (q--) {
        int op;
        cin >> op;
        if (op == 1) {
            int u, v;
            cin >> u >> v;
            --u, --v;
            d.unite(u, v);
        } else {
            int v, k;
            cin >> v >> k;
            --v;
            int fa = d.get(v);
            if (d.node[fa].size() < k) {
                cout << -1 << '\n';
            } else {
                auto it = d.node[fa].rbegin();
                advance(it, k - 1);
                cout << *it + 1 << '\n';
            }
        }
    }

    return 0;
}



F - Teleporting Takahashi 2 (abc372 F)

题目大意

给定一张有向图,它首先是个环,然后在此基础上加了\(m \leq 50\)条有向边。

问从 \(1\)号点出发,走 \(k\)个点,所形成的点的路径 \((1,v_1,v_2,...,v_k)\)的情况数。

解题思路

考虑朴素搜索,设\(dp[u][k]\)表示从 \(u\)号点走 \(k\)步的方案数,显然其答案就是 \(\sum_{u \to v}dp[v][k-1]\)。

但这复杂度是 \(O(nk)\),显然过不了。

但注意到有很多点 \(u \to v\)的 \(v\)其实只有一个即 \(u+1\),即只有一种走法,它们的转移显然是没必要的。我们可以只考虑有分叉点的 \(u\)。而这有分叉点的 \(u\)最多只有 \(m\)个。因此如果我们的转移是从 $u \to $分叉点,这样的时间复杂度就是 \(O(mk)\), 是可以通过的。

记当前点\(u\),然后找到 \(u\)后第一个分叉点 \(x\), 其出度\(> 1\),那么 \(dp[u][k] = \sum_{x \to y}dp[y][k^\prime]\),转移后\(k^\prime\)可以通过简单的运算得到,即\(k - dis(u \to x) - 1\)。

有效的\(u\)就只有 \(2m\)个,对 \(dp\)进行记忆化搜索,复杂度即为 \(O(mk)\)。因为用\(map\)来记忆化会超时,所以只好离散化有效的 \(u\),转成数组来记忆化了。

神奇的代码
#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, m, k;
    cin >> n >> m >> k;
    vector<vector<int>> edge(n);
    for (int i = 0; i < n; i++) {
        edge[i].push_back((i + 1) % n);
    }
    vector<int> tr;
    for (int i = 0; i < m; i++) {
        int u, v;
        cin >> u >> v;
        u--;
        v--;
        edge[u].push_back(v);
        tr.push_back(u);
    }
    for (int i = 0; i < n; i++) {
        sort(edge[i].begin(), edge[i].end());
        edge[i].erase(unique(edge[i].begin(), edge[i].end()), edge[i].end());
    }
    sort(tr.begin(), tr.end());
    tr.erase(unique(tr.begin(), tr.end()), tr.end());
    auto calc = [&](int u, int v) -> int {
        if (u > v)
            v += n;
        return v - u;
    };
    int ans = 0;
    if (m == 0) {
        ans = 1;
    } else {
        vector<int> nxt(n);
        for (int i = 0; i < n; i++) {
            auto it = lower_bound(tr.begin(), tr.end(), i);
            if (it == tr.end())
                it = tr.begin();
            nxt[i] = *it;
        }
        vector<int> id(n, -1);
        int cnt = 0;
        auto dfs_pre = [&](auto& dfs, int u) -> void {
            if (id[u] != -1)
                return;
            id[u] = cnt++;
            int it = nxt[u];
            for (int v : edge[it]) {
                dfs(dfs, v);
            }
        };
        dfs_pre(dfs_pre, 0);
        vector<vector<int>> dp(k + 1, vector<int>(cnt, -1));

        auto dfs = [&](auto& dfs, int u, int k) -> int {
            if (k == 0) {
                return 1;
            }
            if (dp[k][id[u]] != -1)
                return dp[k][id[u]];
            int it = nxt[u];
            int sum = 0;
            int nxt = (u + 1) % n;
            int cost = calc(u, it);
            if (cost >= k)
                return 1;
            for (int v : edge[it]) {
                sum += dfs(dfs, v, k - cost - 1);
                if (sum >= mo)
                    sum -= mo;
            }
            return dp[k][id[u]] = sum;
        };
        ans = dfs(dfs, 0, k);
    }
    cout << ans << '\n';

    return 0;
}



G - Ax + By < C (abc372 G)

题目大意

给定三个\(n\)个数的数组\(a,b,c\),问 \((x,y)\)的数量,满足

  • \(a_i x + b_i < c\),对任意 \(i \in [1,n]\)

多组数据。

解题思路

一眼只会\(O(na_i)\)。

神奇的代码



标签:AtCoder,return,Beginner,int,tr,cin,dfs,vector,372
From: https://www.cnblogs.com/Lanly/p/18424613

相关文章

  • UNIQUE VISION Programming Contest 2024 Autumn (AtCoder Beginner Contest 372)
    总结(我的塘人局):单调栈是忘得差不多了 A-delete.题意:输出删除所有'.'的字符串思路:遍历输出不是'.'复杂度:O(n) Code:#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;usingi64=int64_t;voidsolve(){strings;cin......
  • ABC372 (D,E)
    ABC372(D,E)D一道比较简单的二分查找题目。观察到每个数能成为\(j\)的条件是独立的,因此想到统计每个数能成为它前面哪些数的\(j\)。对于每个\(ed​\),二分\(1\simed-1​\)中最后一个大于\(h[ed]​\)的数的位置\(st​\),那么\(h[ed]​\)可作为\(st\simed-1......
  • 设计资料保存:372-基于XC7VX690T的万兆光纤、双FMC扩展的综合计算平台 RISCV 芯片验证
      一、板卡概述      基于V7的高性能PCIe信号处理板,板卡选用Xilinx 公司Virtex7系列FPGA XC7VX690T-2FFG1761C为处理芯片,板卡提供两个标准FMC插槽,适用于高性能采集、回放以及相关处理。通过连接不同的FMC子卡的方式,可实现不同形式的数据采集、回放、处理的功能模块。板......
  • [atcoder agc 004 c] AND Grid
    题目链接题目简述给定一个H×WH\timesWH×W的网格图,有些位置已经被涂色。要求构造两个相同大小的网格图......
  • ECON 3720: Introduction to Econometrics
    ECON 3720: Introduction to EconometricsProblem Set 02Fall Semester 2024Due: September 20th 2024Please submit the problem set no later than 5 PM on September 20th 2024. Submit the problem set to your TA’s mailbox in th......
  • 设计方案:372-基于7VX690T的万兆光纤、双FMC扩展的综合计算平台 RISCV 芯片验证平台
    基于7VX690T的万兆光纤、双FMC扩展的综合计算平台RISCV芯片验证平台 一、板卡概述      基于V7的高性能PCIe信号处理板,板卡选用Xilinx 公司Virtex7系列FPGA 7VX690T-2FFG1761C为处理芯片,板卡提供两个标准FMC插槽,适用于高性能采集、回放以及相关处理。通过连接不同的FMC......
  • AtCoder Beginner Contest 371
    A-Jiro输入\(AB,BC,AC\)的大小关系,输出中位数。手动模拟一下。点击查看代码#include<bits/stdc++.h>usingnamespacestd;#definelllonglong#defineullunsignedlonglongintread(){intx=0;boolf=false;charc=getchar();while(c<'......
  • AtCoder Beginner Contest 371
    A-Jiro(abc371A)题目大意三个人,给定他们相互之间的大小关系。问谁在中间。解题思路排个序,大小结果就是题目给的,因为问的是中间是谁,所以写的时候并没在意是升序排还是降序排。神奇的代码#include<bits/stdc++.h>usingnamespacestd;usingLL=longlong;intmai......
  • AtCoder Beginner Contest 371
    ABC371总结AtCoderBeginnerContest371一些废话想着以后换一种方式写总结,不再以那种题解形式,写起来又累又难写,只对其中部分有意思的题目写出完整的题解。就是以随笔的形式,在打完比赛后写出自己的一些感悟,每道题做的过程中的一些思路、用时和需要改进的地方,就是类似随笔之类的......
  • AtCoder Beginner Contest 371
    https://atcoder.jp/contests/abc371C:暴力。思路是把1-8的点映射到全排列上面,然后把有的点去掉没的点加上取ans最小值。这题复杂度是\(8!\times7\times4\),暴力求全排列即可(第一次写暴力全排列思索了一会复杂度#include<bits/stdc++.h>usingnamespacestd;#definepiipai......