Educational Codeforces Round 163 (Div. 2) A~F

A. Special Characters (思维)

题意:

给你一个整数 n n n 。构造一个由大写字母组成的字符串。这个字符串中必须有 n n n个特殊字符。如果一个字符与其相邻的一个字符相等,我们就将其称为特殊字符。
例如,在 AAABAACC 字符串中有 6 6 6 个特殊字符(分别位于 1 1 1 3 3 3 5 5 5 6 6 6 7 7 7 8 8 8 )。
输出任何满足题意的字符串。

分析:

奇数情况无解。偶数情况通过 A A B B AABB AABB循环的形式进行构造。

代码:

#include <bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        if (n & 1)
            cout << "NO" << endl;
        else {
            cout << "YES" << endl;
            string ans;
            for (int i = 1, j = 1; i <= n; i += 2, j++) {
                if (j & 1)
                    ans += "AA";
                else
                    ans += "BB";
            }
            cout << ans << endl;
        }
    }
    return 0;
}

B.Array Fix (贪心)

题意:

给你一个长度为 n n n 的整数数组 a a a

你可以执行以下操作任意多次(可能为零):取数组 a a a 中至少是 10 10 10 的任意元素,删除它,然后在相同位置插入该元素包含的数字,按它们在该元素中出现的顺序排列。

  • 如果我们对数组 [ 12 , 3 , 45 , 67 ] [12, 3, 45, 67] [12,3,45,67] 中的 第 3 3 3个元素执行此操作,那么数组就变成了 [ 12 , 3 , 4 , 5 , 67 ] [12, 3, 4, 5, 67] [12,3,4,5,67]
  • 如果我们对数组 [ 2 , 10 ] [2, 10] [2,10] 中的 第 2 2 2个元素执行此操作,那么数组就变成了 [ 2 , 1 , 0 ] [2, 1, 0] [2,1,0]

你的任务是确定是否有可能通过任意次上述操作使 a a a 以非降序排序。换句话说,你必须确定是否有可能将数组 a a a 转换为 a 1 ≤ a 2 ≤ ⋯ ≤ a k a_1 \le a_2 \le \dots \le a_k a1a2ak ,其中 k k k 是数组 a a a 的当前长度。

分析:

我们从后往前倒推,记录前一个的值,如果比当前值大就尝试拆当前值。拆了值只会变小,所以尽可能不拆。

代码:

#include <bits/stdc++.h>

using namespace std;
const int INF = 1e9;
int a[105];

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++)
            cin >> a[i];
        bool flag = 1;
        int r = INF;
        for (int i = n - 1; i >= 0 && flag; i--) {
            int x = a[i];
            if (a[i] <= r)
                r = a[i];
            else {
                while (x) {
                    int y = x % 10;
                    if (y <= r)
                        r = y;
                    else {
                        flag = 0;
                        break;
                    }
                    x /= 10;
                }
            }
        }
        if (flag)
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }
    return 0;
}

C. Arrow Path (bfs)

题意:

有一个网格,由 2 2 2 行和 n n n 列组成。行的编号从上到下从 1 1 1 2 2 2 。列的编号从左到右依次为 1 1 1 n n n 。网格的每个单元格都包含一个箭头,指向左边或右边。没有箭头指向网格外。

有一个机器人从 ( 1 , 1 ) (1, 1) (1,1) 格开始。每隔一秒钟,下面两个动作会相继发生:

  1. 首先,机器人向左、向右、向下或向上移动(不能试图移动到网格外,也不能跳过移动);
  2. 然后,机器人沿着放置在当前单元格中的箭头移动。

判断机器人能否到达 ( 2 , n ) (2, n) (2,n) 单元格。

分析:

b f s bfs bfs的过程中,将每一步加入队列的过程改成每两步加入一次队列即可。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
string mp[2];
int vis[2][N], dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
unordered_map<char, int> dir;

int main() {
    int t;
    cin >> t;
    dir['<'] = 3;
    dir['>'] = 1;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 0; i < 2; i++) {
            cin >> mp[i];
        }
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < n; j++) {
                vis[i][j] = 0;
            }
        }
        vis[0][0] = 1;
        queue<pair<LL, LL> > q;
        q.push({0, 0});
        bool flag = false;
        while (q.size()) {
            auto tmp = q.front();
            q.pop();
            int x = tmp.first, y = tmp.second;
            if (x == 1 && y == n - 1) {
                flag = 1;
                break;
            }
            for (int i = 0; i < 4; i++) {
                int X = x + dx[i], Y = y + dy[i];
                if (X < 0 || X > 1 || Y < 0 || Y > n - 1)
                    continue;
                int X2 = X + dx[dir[mp[X][Y]]], Y2 = Y + dy[dir[mp[X][Y]]];
                if (X2 < 0 || X2 > 1 || Y2 < 0 || Y2 > n - 1)
                    continue;
                if (!vis[X2][Y2]) {
                    vis[X2][Y2] = 1;
                    q.push({X2, Y2});
                }
            }
        }
        if (flag)
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }
    return 0;
}

D.Tandem Repeats? (dp)

题意:

给你一个由小写拉丁字母或问号组成的字符串 s s s ,串联重复是一个偶数长度的字符串,其前半部分等于后半部分。
现在要求用某个小写英文字母替换每个问号,使串联重复的最长子串的长度尽可能最大。

分析:

d p [ i ] [ j ] dp[i][j] dp[i][j]表示从 i i i开始的后缀和从 j j j开始的后缀的最大前缀和,当符合 s [ i ] = = ′ ? ′ ∣ ∣ s [ j ] = = ′ ? ′ ∣ ∣ s [ i ] = = s [ j ] s[i] == '?' || s[j] == '?' || s[i] == s[j] s[i]==?∣∣s[j]==?∣∣s[i]==s[j]的时候,有转移方程 d p [ i ] [ j ] = d p [ i + 1 ] [ j + 1 ] + 1 ; dp[i][j] = dp[i + 1][j + 1] + 1; dp[i][j]=dp[i+1][j+1]+1;。再遍历一遍 d p dp dp数组,如果满足 d p [ i ] [ j ] ≥ j − i dp[i][j] \ge j-i dp[i][j]ji,说明以 i i i 开头往后的 2 × ( j − i ) 2 \times (j - i) 2×(ji)子串符合题意。

代码:

#include <bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        string s;
        cin >> s;
        int n = s.size();
        s = " " + s;
        vector<vector<int>> dp(n + 2, vector<int>(n + 2));
        for (int i = n; i >= 1; i--) {
            for (int j = n; j >= 1; j--) {
                if (s[i] == '?' || s[j] == '?' || s[i] == s[j]) {
                    dp[i][j] = dp[i + 1][j + 1] + 1;
                }
            }
        }
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = i + 1; j <= n; j++) {
                if (dp[i][j] >= j - i) {
                    ans = max(ans, (j - i) * 2);
                }
            }
        }
        cout << ans << endl;
    }
    return 0;
}

E.Clique Partition (图论)

题意:

给你两个整数 n n n k k k 。在 n n n 个顶点上有一个图,编号从 1 1 1 n n n ,最初没有边。

现在要为每个顶点分配一个整数;让 a i a_i ai 成为顶点 i i i 上的整数。所有的 a i a_i ai 都应该是从 1 1 1 n n n 的不同整数。
分配整数后,对于每一对顶点 ( i , j ) (i, j) (i,j) ,如果有 ∣ i − j ∣ + ∣ a i − a j ∣ ≤ k |i - j| + |a_i - a_j| \le k ij+aiajk 则在它们之间添加一条边。
现在的目标是创建一个可以分割成尽可能少的(对于给定的 n n n k k k 值)小群的图。图中的每个顶点都应属于一个小群。一个小群是一个顶点集合,其中的每一对顶点都有一条边相连。

分析:

通过观察发现,我们需要选择一个连续的区间作为一个小群,因为连续的区间 ∣ i − j ∣ \vert i-j \vert ij是最小的。通过打表发现对于长度为 x x x的区间, ∣ i − j ∣ + ∣ a i − a j ∣ \vert i-j \vert + \vert a_i-a_j \vert ij+aiaj同样是 x x x。接下来就可以按顺序排列,然后把一半的前缀放到后面去来进行构造。

代码:

#include <bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, k;
        cin >> n >> k;
        vector<int> ans;
        vector<int> color;
        int tmp = min(k, n);
        int cnt = 0;
        for (int i = 1; i <= n; i += tmp) {
            int l = i, r = min(n, i + tmp - 1);
            ++cnt;
            vector<int> vis;
            for (int j = l; j <= r; j++) {
                ans.push_back(cnt);
                vis.push_back(j);
            }
            rotate(vis.begin(), vis.begin() + vis.size() / 2, vis.end());
            color.insert(color.end(), vis.begin(), vis.end());
        }
        for (auto x: color)
            cout << x << " ";
        cout << endl;
        cout << cnt << endl;
        for (auto x: ans)
            cout << x << " ";
        cout << endl;
    }
    return 0;
}

F.Rare Coins (数学)

题意:

n n n 个袋子,编号从 1 1 1 n n n i i i 个袋子里有 a i a_i ai 枚金币和 b i b_i bi 枚银币。
一枚金币的价值是 1 1 1 。一枚银币的价值是 0 0 0 1 1 1 ,由每枚银币独立决定( 0 0 0 的概率是 1 2 \frac{1}{2} 21 1 1 1 的概率是 1 2 \frac{1}{2} 21 )。
回答 q q q 个独立的问题。每个问题如下:

  • l l l r r r - 计算 l l l r r r 袋中硬币的总价值严格大于所有其他袋中硬币总价值的概率。

分析:

计算概率实际上是计算有多少种银币的方案数使得自己更大。假设自己有 x x x枚银币变成 1 1 1,其他有 y y y枚变成 1 1 1。得到下列式子:自己的 a a a金币+ x > x > x>其他袋中的 a a a金币+ y y y。即设定一个约束 x − y > z x - y > z xy>z,满足这个约束下的方案数为 ( b 1 x ) \tbinom{b_1}{x} (xb1) ( b 2 y ) \tbinom{b_2}{y} (yb2)

b 1 , b 2 b_1 , b_2 b1,b2分别表示自己有的银币和其他袋子里的银币。上述式子在形式上和范德蒙德卷积类似,只需要将 x − y x - y xy改成 x + y x + y x+y即可。我们修改假设为,有 y y y枚变成 0 0 0,约束变为 a 1 + x > a 2 + b 1 − y a_1 + x > a_2 + b_1 - y a1+x>a2+b1y a 1 , a 2 a_1 , a_2 a1,a2 b 1 , b 2 b_1, b_2 b1,b2定义类似。再由由范德蒙德卷积可知总方案数为 ( n z + 1 ) + … ( n n ) \tbinom{n}{z+1}+ \dots \tbinom{n}{n} (z+1n)+(nn) n n n表示银币的总数。预处理后缀和之后即可进行计算。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const LL mod = 998244353;
const int N = 1e6 + 5;

LL binpow(LL a, LL n) {
    LL ans = 1;
    while (n) {
        if (n & 1)
            ans = (ans * a) % mod;
        a = (a * a) % mod;
        n >>= 1;
    }
    return ans;
}

LL inv(LL x) { return binpow(x, mod - 2); }

int a[N], b[N];
int prea[N], preb[N];
LL com[N], suf[N], fact[N], invfact[N];

int main() {
    ios::sync_with_stdio(false);
    int n, q, l, r;
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        prea[i] = prea[i - 1] + a[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
        preb[i] = preb[i - 1] + b[i];
    }
    com[0] = 1;
    fact[0] = 1;
    for (int i = 1; i <= preb[n]; i++)
        fact[i] = (fact[i - 1] * i) % mod;
    invfact[preb[n]] = inv(fact[preb[n]]);
    for (int i = preb[n] - 1; i >= 0; i--)
        invfact[i] = (invfact[i + 1] * (LL) (i + 1)) % mod;
    for (int i = 1; i <= preb[n]; i++)
        com[i] = fact[preb[n]] * invfact[preb[n] - i] % mod * invfact[i] % mod;
    suf[preb[n]] = com[preb[n]];
    for (int i = preb[n] - 1; i >= 0; i--)
        suf[i] = (suf[i + 1] + com[i]) % mod;
    LL invs = inv(binpow(2, preb[n]));
    for (int i = 1; i <= q; i++) {
        cin >> l >> r;
        int ina = prea[r] - prea[l - 1], inb = preb[r] - preb[l - 1];
        int outa = prea[n] - ina, outb = preb[n] - inb;
        int condition = max(outa + outb - ina, -1);
        LL ans = (condition >= preb[n] ? 0 : suf[condition + 1]);
        ans = (ans * invs) % mod;
        cout << ans << " ";
    }
    cout << endl;
    return 0;
}

赛后交流

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值