Educational Codeforces Round 123 (Rated for Div. 2) (A-D)

A. Doors and Keys

题意:在水平轴上有三个有颜色的门以及其对于颜色的钥匙,要穿过门必须要拿到相应颜色的钥匙才能通过,给出门和钥匙在水平轴上的分布,询问是否能穿过这三条门。

题解:模拟即可,遇到门时查看是否有钥匙就行,没有就是不能穿过这三条门,否则当前这条门就可通过。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef long long ll;

inline int read()
{
    int x = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= ch=='-', ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return f ? -x : x;
}

int main()
{    
    int t = read();
    while (t -- )
    {
        string s;
        cin >> s;
        int r = 0, g = 0, b = 0;
        bool flag = true;
        for (int i = 0; i < s.size(); i ++ )
        {
            if (s[i] == 'R' && !r) flag = false;
            if (s[i] == 'G' && !g) flag = false;
            if (s[i] == 'B' && !b) flag = false;
            if (s[i] == 'r') r ++;
            if (s[i] == 'b') b++;
            if (s[i] == 'g') g ++;
        }
        if (flag) puts("YES");
        else puts("NO");
    }
    return 0;
}

B. Anti-Fibonacci Permutation

题意:求n个1-n的全排序序列是反斐波拉契的数列,反斐波拉契数列a定义为ai-2 + ai-1 != ai(3 <= i <= n)

题解:我们可以先把最大的偶数单独拿出来,将剩下的数按逆序排列,这n个序列即是把这个最大的偶数分别插入这n-1的数的n个空隙中。
证明:
显然在把这个最大偶数插进这个序列之前这个序列一定是满足要求的(前一个数一定大于后一个数即ai-1 > ai, 所以ai-2 + ai-1 > ai),因此我们只要考虑插入之后是否会满足上述方程,假设这个最大的偶数是x,跟x相关的序列为 a, b, x, c, d(a > b > c > d), 我们只要判断a + b == x, b + x == c, x + c==d是否成立即可,在未插入之前相邻两项的和最多有一项为偶数(即为x-1和x+1),对于a + b是奇数的显然a + b != x, 对于a + b是偶数的显然a = x - 1, b = x + 1, 而此时 a + b == 2x因此a + b != x成立,对于b + x == c 和 x + c == d,显然b > c, c > d, 因此b + x > c, x + c > d, 即b + x != c, x + c != d;

代码:

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef long long ll;

inline int read()
{
    int x = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= ch=='-', ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return f ? -x : x;
}

int main()
{    
    int t = read();
    while (t -- )
    {
        int n;
        cin >> n;
        int x = n, y = n - 1;  // x为最大偶数, y为剩下的数里面的最大值
        if (n & 1) x --, y = n; // n是奇数,则x对于n-1
        for (int i = 1; i <= n; i ++ )
        {
            int cnt = y;
            for (int j = 1; j <= n; j ++ )  // 插入空隙的位置
            {
                if (i == j) printf("%d ", x);
                else if (cnt == x)
                { // x已经安排了位置,因此安排下一个数
                    cnt --;
                    printf("%d ", cnt --);
                }
                else printf("%d ", cnt--);
            }
            puts("");
        }
    }
    return 0;
}

C. Increase Subarray Sums

题意:给定一个长度为n的a数组以及一个数x,定义f[k]表示将数组a中恰好k个数的值加上x之后的最大的连续区间的和, 求f[0], f[1], … f[n]

题解:首先一种暴力的做法就是把所有区间的和求出来,然后一个一个区间的去比较找到最大值,但是时间复杂度达到了n^3(区间数量有(n ^2 + n)/2 个)。但是我们对于相同长度的区间,我们没有必要都用到,只需要用到最大和即可,假设长度为len的区间和为sum1, sum2, …, sumn, 由于x>=0,因此x加的数量越多越好,设y = min(k, len), 即对于这个长度的区间而言求max(sum1+y * x, sum2+y * x, … , sumn + y * x) = max(sum1, sum2, …, sumn) + y * x, 明显我们只需要这个区间长度和的最大值即可。因此我们可以预处理出每个长度的区间的最大的和数组ma, ma[i]表示长度为i的区间的最大和,因此f[i] = max(ma[1]+min(1, i)*x, ma[2] + min(2, i) * x, .... , ma[n] + min(n, i)),因此第一层循环枚举k(0->n),第二层枚举区间长度(1->n),因此时间复杂度就变成了n^2
代码:

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef long long ll;
const int N = 5010;
ll ma[N];
ll sum[N];
inline int read()
{
    int x = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= ch=='-', ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return f ? -x : x;
}

int main()
{    
    int t = read();
    while (t -- )
    {
        int n = read(), m = read();
        for (int i = 1; i <= n; i ++ )
        {
            ma[i] = -1e9;
            sum[i] = read();
            sum[i] += sum[i - 1];
        }
        for (int i = 1; i <= n; i ++ )
            for (int j = 0; j < i; j ++ )
                ma[i-j] = max(ma[i-j], sum[i]-sum[j]);

        for (int i = 0; i <= n; i ++ )
        {
            ll ans = 0;
            // 加的值越多越好,但是不能大于区间长度或者大于当前的j
            for (int j = 1; j <= n; j ++ ) ans = max(ans, ma[j] + min(i, j)*m);  
            printf("%lld%c", ans, " \n"[i==n]);
        }
    }
    return 0;
}

D. Cross Coloring

题意:给出一个n*m的没有颜色的网格以及k种涂料,有q个操作,每个操作给出一个(x, y), 即从k中涂料中选出一种然后把第x行和第y列的网格全部涂刷乘这种颜色,问q次操作完成后有第三种不同的n * m网格

题解:从后往前考虑,设cnt为对于操作i(1 <= i <= n)其后面的涂刷操作没有完全覆盖xi行和y列的数量,答案即是k ^ cnt。
证明:如果操作i其后面的涂刷操作没有完全覆盖xi行和y列的数量, 说明第i次操作可以完全主导这些没有被后面覆盖的格子可以任意的填而与其他的操作无关,因此其有k个涂法,答案即使cnt个k相乘k^cnt

代码:

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef long long ll;
const int N = 2e5 + 10, mod = 998244353;
PII g[N];

inline int read()
{
    int x = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= ch=='-', ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return f ? -x : x;
}

int main()
{    
    int t = read();
    while (t -- )
    {
        int n = read(), m = read(), k = read(), q = read();
        for (int i = 1; i <= q; i ++ )
        {
            int x = read(), y = read();
            g[i] = {x, y};
        }
        set<int> r, c;
        ll ans = 1;
        for (int i = q; i ; i -- )
        {
            bool flag = false;
            if (!r.count(g[i].first))  // 行没有被完全覆盖
            {
                r.insert(g[i].first);
                flag = true;
            }
            if (!c.count(g[i].second))  // 列没有被完全覆盖
            {
                c.insert(g[i].second);
                flag = true;
            }
            if (flag) ans = ans * k % mod;
            if (r.size() == n || c.size() == m) break; // 整个格子都已被后面的操作覆盖了
        }
        printf("%lld\n", ans);
    }
    return 0;
}

完结,如有错误还请大佬指正!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值