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

AtCoder Beginner Contest 381

时间:2024-11-25 20:24:28浏览次数:4  
标签:AtCoder cnt return Beginner 字符 int auto cin 381

省流版
  • A. 按题意判断即可
  • B. 按题意判断即可
  • C. 枚举/的位置,然后分别向左右找到最长的1串和2串,然后取最小值即可
  • D. 讨论起始位置的奇偶性,然后用双指针,每两个字符每两个字符,维护出现的次数为2,两种情况取最大值即可
  • E. 答案为所有/的左右12个数的最小值的最大值,注意到个数随着/的增大是单调的,而最大值出现在交点,二分找该交点即可
  • F. \(dp[i]\)表示已经选择的字符的集合状态是\(i\)时的最小的选择字符的位置,转移枚举下一个字符,然后找到下一个字符的位置,然后更新\(dp\)即可

A - 11/22 String (abc381 A)

题目大意

给定一个字符串,问它是不是形如11/22的形式。即一个1串和一个2串,长度相等,中间用/分隔。

解题思路

找到/,然后判断左右两边是否是1串和2串即可。

神奇的代码
n = input()
s = input()
s = s.split('/')
if len(s) == 2 and len(s[0]) == len(s[1]) and all(i == '1' for i in s[0]) and all(i == '2' for i in s[1]):
    print('Yes')
else:
    print('No')


B - 1122 String (abc381 B)

题目大意

给定一个字符串,问它是不是形如11223344的形式。即每两位相同,且每个字符出现的次数为2。

解题思路

判断奇数位和偶数位是否相同,然后判断每个字符出现的次数是否为2即可。

神奇的代码
s = input()
c = set(s)
if s[::2] == s[1::2] and all(sum(1 for i in s if i == j) == 2 for j in c):
    print('Yes')
else:
    print('No')


C - 11/22 Substring (abc381 C)

题目大意

给定一个字符串,问它的最长子串是多少,使得子串中1的个数和2的个数相等,中间用/分隔。

解题思路

枚举/的位置,然后分别向左右找到最长的1串和2串,然后取最小值即可。

因为每个子串只包含一个/,因此字符串就被/分成若干部分,每个部分最多只会遍历两次,因此时间复杂度为\(O(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;
    string s;
    cin >> n >> s;
    int ans = 0;
    auto find1 = [&](int x) {
        for (int i = x; i >= 0; --i) {
            if (s[i] != '1')
                return x - i;
        }
        return x + 1;
    };
    auto find2 = [&](int x) {
        for (int i = x; i < n; ++i) {
            if (s[i] != '2')
                return i - x;
        }
        return n - x;
    };
    for (int i = 0; i < n; i++) {
        if (s[i] == '/') {
            ans = max(ans, min(find1(i - 1), find2(i + 1)));
        }
    }
    ans = ans * 2 + 1;
    cout << ans << '\n';

    return 0;
}



D - 1122 Substring (abc381 D)

题目大意

给定一个字符串,问它的最长子串是多少,使得每两位相同,且每个字符出现的次数为2。

解题思路

如果每两个看成一个字符,问题其实就是求最长的子串,其每个字符出现的次数为1。这是一个经典的双指针问题,即维护一个区间\(l,r\),使得区间内的每个字符出现的次数为1,然后不断向右移动\(r\),直到区间内的字符出现的次数不满足条件,然后向右移动\(l\),直到区间内的字符出现的次数满足条件,然后继续向右移动\(r\),直到字符串结束。因为每个字符最多只会被访问两次,因此时间复杂度为\(O(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<int> a(n);
    for (auto& i : a) {
        cin >> i;
        --i;
    }
    auto solve = [&](int st) {
        int ret = 0;
        int la = st;
        vector<int> cnt(n);
        for (int i = st; i < n; i += 2) {
            if (i + 1 >= n || a[i] != a[i + 1] || cnt[a[i]] == 1) {
                ret = max(ret, i - la);
                while (la < i && (a[i] != a[i + 1] || cnt[a[i]] == 1)) {
                    cnt[a[la]]--;
                    la += 2;
                }
            }
            if (a[i] == a[i + 1])
                cnt[a[i]]++;
            else
                la += 2;
        };
        ret = max(ret, n - la - ((n - st) & 1));
        return ret;
    };
    int ans = max(solve(0), solve(1));
    cout << ans << '\n';

    return 0;
}



E - 11/22 Subsequence (abc381 E)

题目大意

给定一个字符串\(s\),给定\(q\)个询问,每个询问给定\(l,r\),问\(s[l,r]\)的最长子序列,使得子序列中1的个数和2的个数相等,中间用/分隔。

解题思路

对于每个询问,枚举/的位置\(m\),答案就是\([l,m]\)的\(1\)的个数和\([m+1,r]\)的\(2\)的个数的最小值的两倍加一。后者个数的计算可以通过前缀和来实现。但前者枚举的复杂度是\(O(n)\),因此总的复杂度是\(O(nq)\),无法通过。

注意答案是\(\min(cnt1[l, m], cnt2[m, r])\),其中\(cnt1[l, m]\)表示\([l,m]\)的\(1\)的个数,\(cnt2[m, r]\)表示\([m+1,r]\)的\(2\)的个数。随着\(m\)的增大,\(cnt1[l, m]\)是单调递增的,\(cnt2[m, r]\)是单调递减的。

因此两者最小值,首先是取在\(cnt1\),然后再变成在\(cnt2\),而其最大值就在这两者的交点。因此可以通过二分来找到这个交点。这样就可以将复杂度降到\(O(q\log n)\)。

虽然这个\(\min\)是个单峰函数,但由于有相同的值,不能直接三分找到最大值。

神奇的代码
#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;
    array<vector<int>, 2> cnt{vector<int>(n), vector<int>(n)};
    vector<int> pos;
    for (int i = 0; i < n; i++) {
        if (s[i] == '/')
            pos.push_back(i);
        else
            cnt[s[i] - '1'][i] = 1;
    }
    partial_sum(cnt[0].begin(), cnt[0].end(), cnt[0].begin());
    partial_sum(cnt[1].begin(), cnt[1].end(), cnt[1].begin());
    auto get_sum = [&](int l, int r, int c) {
        return cnt[c][r] - (l - 1 < 0 ? 0 : cnt[c][l - 1]);
    };
    auto solve = [&](int L, int R) {
        auto calc1 = [&](int x) { return get_sum(L, x, 0); };
        auto calc2 = [&](int x) { return get_sum(x, R, 1); };
        auto check = [&](int x) { return calc1(x) <= calc2(x); };

        int l = lower_bound(pos.begin(), pos.end(), L) - pos.begin();
        int r = upper_bound(pos.begin(), pos.end(), R) - pos.begin();
        if (l == r)
            return 0;
        int ret = 0;
        while (l + 1 < r) {
            int m = (l + r) / 2;
            if (check(pos[m]))
                l = m;
            else
                r = m;
        }
        for (int i = l; i <= r; i++) {
            if (L <= pos[i] && pos[i] <= R)
                ret = max(ret, min(calc1(pos[i]), calc2(pos[i])));
        }
        return ret * 2 + 1;
    };
    while (q--) {
        int l, r;
        cin >> l >> r;
        --l, --r;
        int ans = solve(l, r);
        cout << ans << '\n';
    }

    return 0;
}



F - 1122 Subsequence (abc381 F)

题目大意

给定一个字符串,问它的最长子序列是多少,使得每两位相同,且每个字符出现的次数为2。

解题思路

注意字符只有\(20\)种。一种朴素搜索就是花\(O(20!)\)枚举字符出现的顺序,一旦顺序确定好了,就是可行性判断,找出这个子序列就可以用贪心的方法,即每次找到一个字符,然后找到下一个字符,直到找到所有的字符。这样的复杂度是\(O(n)\)的。

考虑如何优化呢,找到其中重复的部分,比如考虑排列123 4567以及213 4567以及312 4567,我们要依次判断这些排列的可行性,比如我们找到4,就要从上一次的位置开始找,对于123213312,这所谓的上一次的位置可能是不一样的,但因为我们要找4,自然希望上一次位置越靠前越好。而对于我们找4还是找5,取决于我们之前是否选择了4。因此这里可以把排列的信息压缩掉,即记\(dp[i]\)表示已经选择的字符的集合状态是\(i\)时的最小的选择字符的位置。比如状态\(i\)表示选择了123,那\(dp[i]\)就是选择顺序123, 132, 213, 231, 312, 321按照上述贪心方法找到的子序列最大下标的最小值。

转移就是枚举下一个字符,然后找到下一个字符的位置,然后更新\(dp\)即可。时间复杂度是\(O(2^{20} 20)\)的。

神奇的代码
#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;
    constexpr int num = 20;
    array<vector<int>, num> pos;
    for (int i = 0; i < n; i++) {
        int a;
        cin >> a;
        --a;
        pos[a].push_back(i);
    }
    constexpr int up = (1 << num);
    vector<int> dp(up, n);
    dp[0] = 0;
    int ans = 0;
    for (int i = 0; i < up; ++i) {
        for (int j = 0; j < num; ++j) {
            if ((i >> j) & 1) {
                int la = i ^ (1 << j);
                auto it = lower_bound(pos[j].begin(), pos[j].end(), dp[la]);
                if (it != pos[j].end() && next(it) != pos[j].end()) {
                    dp[i] = min(dp[i], *next(it));
                    ans = max(ans, __builtin_popcount(i));
                }
            }
        }
    }
    cout << ans * 2 << '\n';

    return 0;
}



G - Fibonacci Product (abc381 G)

题目大意

定义\(a_1 = x, a_2 = y, a_i = a_{i-1} + a_{i - 2}\)。

求\(\prod_{i=1}^{n} (a_i) \mod 998244353\)

解题思路

<++>

神奇的代码



标签:AtCoder,cnt,return,Beginner,字符,int,auto,cin,381
From: https://www.cnblogs.com/Lanly/p/18568552

相关文章

  • AtCoder ABC321F - #(subset sum = K) with Add and Erase 题解 可撤销背包
    题目链接:https://atcoder.jp/contests/abc321/tasks/abc321_f题目大意:给定大小为\(k\)的背包和\(q\)次操作,支持两种操作:插入一个大小为\(x\)的元素;删除一个大小为\(x\)的元素。每次操作后,求装满背包方案数。解题思路:可撤销背包。插入\(x\)时,fori=K->x......
  • Toyota Programming Contest 2024#11(AtCoder Beginner Contest 379)
    A-Cyclic链接:A-Cyclic代码:#include<bits/stdc++.h>usingnamespacestd;intmain(){ stringss; cin>>ss; cout<<ss[1]<<ss[2]<<ss[0]<<""<<ss[2]<<ss[0]<<ss[1]; return0;}B-Strawberri......
  • 题解:AT_abc381_c [ABC381C] 11/22 Substring
    显然这个“11/22Substring”是以那个“/”为中心对称的。鉴于一个这样的字符串只能有一个“/”,而题目又要求最长,所以确定了“/”就能确定一个满足要求的子串。那思路就很简单了,只有两步:找到所有的“/”两边同时寻找相应的子串。别的,除了判断一下越界之外,就不用管了。......
  • MATH38161 Multivariate Statistics and Machine Learning
    MATH38161MultivariateStatisticsandMachineLearningCourseworkovember2024OverviewThecourseworkisadataanalysisprojectwithawrittenreport.YouwillapplyskillsandtechniquesacquiredfromWeek1toWeek8toanalyseasubsetoftheFMNISTda......
  • 【Atcoder训练记录】AtCoder Beginner Contest 381
    训练情况赛后反思简单题A题做红温了,怒吃6罚时,C题双指针其实差不多想出来了,但是对于判断字符串合法其实可以只判断两个端点,不需要全部遍历,中途还想了二分做法(?),然而写到最后发现并没有二分单调性。A题记得判断字符串的长度必须是奇数,\(1\sim\frac{n+1}{2}-1\)是1,\(\frac{......
  • AtCoder Beginner Contest 381
    这场比赛打的冷汗直流,然后无奈寄掉。C-11/22Substring本以为直接暴力就可以,但是需要加前缀和优化,一个正向处理,一个反向处理,然后查找/。abc381_d赛前2分钟hack掉自己的代码,然后寄掉。双指针答案必须是连续的区间,所以想到双指针维护区间合法性,但需要处理以下细节:\(a_r\n......
  • BeginnersBook-Perl-教程-一-
    BeginnersBookPerl教程(一)原文:BeginnersBook协议:CCBY-NC-SA4.0Perl-列表和数组原文:https://beginnersbook.com/2017/05/perl-lists-and-arrays/在Perl中,人们可以交替使用术语列表和数组,但是存在差异。列表是数据(标量值的有序集合),数组是保存列表的变量。如何定......
  • C - Word Ladder (Toyota Programming Contest 2024#9 (AtCoder Beginner Contest 370)
    题目链接:C-WordLadder题目:样例:分析:不要被题目所吓到,一切长题目都是纸老虎。题目大意就是给你两个字符串s和t,一次只能更换一个字母,求s变到t更换的次数,并输出每次更换一个字母后的最小字典序字符串。题意好理解,可以直接暴力,大力出奇迹。但是有没有更好的方法呢?既然问了......
  • FL Studio 24.1.2.4381中文版免费下载及FL Studio 24最新使用学习教程
    家好呀,作为一个资深的音乐爱好者和制作人,今天我要安利一个我最近超级痴迷的数字音频工作站软件——FLStudio24.1.2.4381中文版。这款产品可是让我的音乐创作之路如虎添翼,快来跟我一起看看它的炫酷功能吧!最近接到很多小伙伴的私信,都在问我平时会使用哪些音乐软件,能不能给一......
  • Atcoder Beginner Contest 372
    AtcoderBeginnerContest372A模拟即可。#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;voidsolve(){charch;while(cin>>ch){if(ch!='.'){cout<<ch;}}}......