Educational Codeforces Round 166(Div.2) A~D

A.Verify Password(字符串)

题意:

Monocarp正在开发他的新网站,目前面临的挑战是如何让用户选择强密码。

Monocarp认为,强密码应满足以下条件:

  • 密码只能由小写拉丁字母和数字组成;
  • 字母后面不能有数字(因此,每个字母后面要么有另一个字母,要么字符串结束);
  • 所有数字应按非递减顺序排序;
  • 所有字母应按非递减顺序排序。

请注意,密码可以只有字母或数字。

Monocarp成功地实现了第一个条件,但他在其余条件上很吃力。您能帮他验证密码吗?

分析:

按照ASCIIASCIIASCII值进行比较(因为字母的ASCIIASCIIASCII本来就在数字后面)。只要找到前面比后面的数大就输出NONONO,反之YESYESYES

代码:

#include<bits/stdc++.h>
using namespace std;

void solve(){
    int n;
    cin>>n;
    string s;
    cin>>s;
    bool flag = true;
    for (int i = 1; i < n; i++){
        if (isalpha(s[i - 1]) && isdigit(s[i])){
            flag = false;
            break;
        }
        if(s[i - 1] > s[i]){
            flag = false;
            break;
        }
    }
    if(flag)
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
}

int main(){
    int t;
    cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

B.Increase/Decrease/Copy(思维)

题意:

给你两个整数数组:长度为nnn的数组aaa和长度为n+1n+1n+1的数组bbb

你可以按任意顺序执行下列操作任意次数:

  • 选择数组aaa中的任意元素,并将其增加111
  • 选择数组aaa中的任意元素,并将其减少111
  • 选择数组aaa中的任意元素,复制并追加到数组aaa的末尾。

你的任务是计算将数组aaa转换为数组bbb所需的最少上述操作次数(可能为零)。可以证明,在问题的限制条件下,这总是可能的。

分析:

对于ai−>bi,1≤i≤na_i->b_i,1\le i\le nai>bi,1in,转变的最小代价就是它们的差值。

对于bn+1b_{n+1}bn+1,得到它的最小代价就是ai−>bia_i->b_iai>bi过程中与它最小的差值+1+1+1

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
#define INF 0x7fffffff

void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    vector<int> b(n + 1);
    for (int i = 0; i < n + 1; i++) {
        cin >> b[i];
    }
    int need = INF;
    LL ans = 0;
    for (int i = 0; i < n; i++) {
        ans += abs(b[i] - a[i]);
        need = min(need, abs(b[n] - b[i]));
        need = min(need, abs(b[n] - a[i]));
        if (a[i] < b[n] && b[n] < b[i] || a[i] > b[n] && b[n] > b[i])
            need = 0;
    }
    cout << ans + need + 1 << endl;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

C.Job Interview(枚举)

题意:

Monocarp打算开一家自己的IT公司。他想招聘nnn名程序员和mmm名测试员。

n+m+1n+m+1n+m+1名候选人,按到达时间顺序从111n+m+1n+m+1n+m+1依次编号。第iii个候选人的编程技能为aia_iai,测试技能为bib_ibi(一个人的编程技能和测试技能是不同的)。团队的技能是所有被聘为程序员的候选人的编程技能之和,以及所有被聘为测试员的候选人的测试技能之和。

当应聘者前来面试时,Monocarp会尝试将其分配到最适合的职位(如果应聘者的编程技能较高,则录用其为程序员,否则录用其为测试员)。如果该职位的所有名额都已招满,Monocarp就会将他们分配到其他职位。

你的任务是,针对每个候选人,计算如果除他们之外的所有人都来面试,团队的技能。请注意,这意味着正好有n+mn+mn+m名候选人来参加面试,因此公司的所有n+mn+mn+m个职位都将被填满。

分析:

从前往后遍历,检查一下是aaa能力值浪费了还是bbb能力值浪费了,然后从后往前枚举,开一个数组维护一下最近后缀损失能力值。输出答案的时候,如果当前的人的站的职位刚好是能力值被浪费的职位,输出总和减去当前的人的能力值加上最近损失能力值。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 1e6 + 10;
LL q[N], p[N], a[N], b[N];

void solve() {
    LL n, m;
    cin >> n >> m;
    LL sum1, sum2, num1, num2, f;
    sum1 = sum2 = num1 = num2 = f = 0;
    for (int i = 0; i <= n + m; i++) {
        cin >> a[i];
        q[i] = p[i] = 0;
    }
    for (int i = 0; i <= n + m; i++) {
        cin >> b[i];
        f += a[i] > b[i];
        if (a[i] > b[i] && num1 <= n || m == i - num1) {
            num1++;
            sum1 += a[i];
            p[i] = 1;
        } else
            sum1 += b[i];
        if (a[i] < b[i] && num2 <= m || n == i - num2) {
            num2++;
            sum2 += b[i];
            q[i] = 1;
        } else
            sum2 += a[i];
    }
    for (int i = 0; i <= n + m; i++) {
        cout << " ";
        cout << (f > n ? (p[i] ? sum1 - a[i] : sum2 - b[i]) : (q[i] ? sum2 - b[i] : sum1 - a[i]));
    }
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

D.Invertible Bracket Sequences(前缀和、双指针)

题意:

正则括号序列是指可以通过在序列的原始字符之间插入字符"1"和"+"来转换成正确算术表达式的括号序列。例如

  • 括号序列"()()“和”(())“是正则表达式(得到的表达式是”(1)+(1)“和”((1+1)+1)");
  • 括号序列")(“、”(“和”)"则不是。

让我们定义括号序列的逆序如下:用’)‘替换所有括号’(‘,反之亦然(用’(‘替换所有括号’)')。例如,字符串"()((“和”)())"互为反义词。

给你一个正则括号序列sss。计算如果将sss中从第lll个字符到第rrr个字符(包括)的子串sss替换为其逆序数,sss仍然是正则括号序列的整数对(l,r)(l,r)(l,r)(1≤l≤r≤∣s∣1\le l\le r\le|s|1lrs)的个数。

分析:

统计序列前iii个有多少个左括号是剩余的,然后思考(l,r)(l,r)(l,r)的选择有何特征:可以发现,若第iii位之前所剩余的左括号跟第jjj位之前所剩余的左括号数量一样,那么(i+1,j)(i+1,j)(i+1,j)这个区间就可能被选择,否则一定不会被选择。

因此我们将剩余左括号数量相同的位置放一起,然后考虑其区间能否真的被选中。通过观察可以发现:若(i,j)(i,j)(i,j)之间位置的剩余左括号数量小于等于第iii位之前所剩余的左括号的两倍,那么这个区间就可以被选中,因此本题转换成RMQRMQRMQ问题。

在枚举位置时,随着起始位置的增大,末尾位置也一定是非递减的,因此用双指针可以将复杂度从O(n2logn)O(n^{2}logn)O(n2logn)变为O(nlogn)O(nlogn)O(nlogn)

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const LL N = 5e05 + 10;

LL n;
vector<LL> a(N, 0);

LL Max[N][21];
LL Min[N][21];

struct ST {
    void init() {
        for (LL i = 1; i <= n; i++) {
            Max[i][0] = a[i];
            Min[i][0] = a[i];
        }
    }

    void work() {
        for (LL j = 1; j <= 21; j++)
            for (LL i = 1; i + (1 << j) - 1 <= n; i++) {
                Max[i][j] = max(Max[i][j - 1], Max[i + (1 << (j - 1))][j - 1]);
                Min[i][j] = min(Min[i][j - 1], Min[i + (1 << (j - 1))][j - 1]);
            }
    }

    LL QueryMax(LL l, LL r) {
        LL k = log2(r - l + 1);
        return max(Max[l][k], Max[r - (1 << k) + 1][k]);
    }

    LL QueryMin(LL l, LL r) {
        LL k = log2(r - l + 1);
        return min(Min[l][k], Min[r - (1 << k) + 1][k]);
    }
};

void solve() {
    string s;
    cin >> s;
    s = "0" + s;
    LL len = s.size();
    n = len + 5;
    a[0] = 0;
    vector<LL> st[len];
    for (LL i = 1; i < len; i++) {
        if (s[i] == '(') {
            a[i] = a[i - 1] + 1;
        } else {
            a[i] = a[i - 1] - 1;
        }
        st[a[i]].push_back(i);
    }
    ST st1;
    st1.init();
    st1.work();
    LL cnt = 0;
    for (LL i = 1; i < len; i++) {
        if (st[i].size() <= 1)
            continue;
        LL len = st[i].size();
        LL r = 0;
        for (LL l = 0; l < len; l++) {
            if (r == len) {
                cnt += (r - l - 1);
                continue;
            }
            LL left = st[i][l];
            LL right = st[i][r];
            while (r < len && st1.QueryMax(left, right) <= i * 2) {
                r++;
                if (r == len) {
                    break;
                }
                right = st[i][r];
            }
            cnt += (r - l - 1);
        }
    }
    cout << cnt << endl;
}

int main() {
    LL t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

赛后交流

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

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

在这里插入图片描述

### 关于Codeforces Educational Round 172 Problem D 的解决方案 对于Codeforces Educational Round 172中的D题,虽然未直接提供该题目具体描述以及官方解答[^2],可以基于过往相似难度和类型的题目给出一般性的解决思路。 #### 题目分析 通常情况下,D级别的题目会涉及到较为复杂的算法设计或是数据结构的应用。这类问题往往需要参赛者具备良好的编程基础、逻辑思维能力以及对特定算法的理解掌握程度。针对不同性质的问题(如图论、动态规划、字符串处理等),采取相应的策略来构建模型并求解是最常见的方法之一。 #### 解决方案框架 假设此题属于某种典型问题类别,则可以根据其特点制定如下通用框架: - **输入解析**:仔细阅读题目说明,明确给定条件与目标函数之间的关系。 - **核心概念理解**:深入剖析题目背后所隐藏的关键知识点或技巧点,这可能涉及但不限于贪心算法、二分查找、树形DP等方面的知识。 - **边界情况考虑**:考虑到极端测试用例的存在,在编写程序时要特别注意各种特殊情况下的行为表现,确保代码鲁棒性强。 - **优化空间复杂度/时间效率**:当面对大数据集时,应尽可能寻找更高效的实现方式减少不必要的计算开销;比如利用哈希表加速查询速度,通过位运算代替常规算术操作提高性能等等。 ```cpp // 假设这是一个简化版的伪代码示例 #include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; // 输入参数 vector<int> data(n); for(auto& d : data){ cin>>d; } // 主体逻辑部分省略... cout << "Result"; return 0; } ``` 由于缺乏具体的题目细节,上述内容仅作为参考模板展示如何着手准备类似的竞赛挑战。为了获得更加精准的帮助建议查阅官方题解文档或者参与社区论坛交流获取更多信息资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值