Codeforces Round 938 (Div. 3) A~F

A.Yogurt Sale(贪心)

题意:

在"Vosmiorochka"商店里,一份酸奶的价格是 a a a布尔,但现在有促销活动,可以用 b b b布尔买到两份酸奶。

马克西姆正好需要购买 n n n份酸奶。在购买两份酸奶时,他可以选择以正常价格或促销价格购买。

马克西姆购买 n n n份酸奶至少需要花费多少布尔?

分析:

两种买法,哪种最省就用哪种买,特判一下奇数情况即可。

代码:

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, a, b;
        cin >> n >> a >> b;
        if (b >= 2 * a) {
            cout << n * a << endl;
        } else {
            int k = n / 2;
            int r = n % 2;
            cout << k * b + r * a << endl;
        }
    }
    return 0;
}

B.Progressive Square(枚举)

题意:

大小为 n n n的前进方阵是一个 n × n n\times n n×n的矩阵。马克西姆选择三个整数 a 1 , 1 a_{1,1} a1,1 c c c d d d并根据以下规则构造一个前进方阵:

a i + 1 , j = a i , j + c a_{i+1,j}=a_{i,j}+c ai+1,j=ai,j+c

a i , j + 1 = a i , j + d a_{i,j+1}=a_{i,j}+d ai,j+1=ai,j+d

例如,如果 n = 3 n=3 n=3 a 1 , 1 = 1 a_{1,1}=1 a1,1=1 c = 2 c=2 c=2 d = 3 d=3 d=3,那么前进方阵的情况如下:

( 1 4 7 3 6 9 5 8 11 ) \begin{pmatrix}1&4&7\\3&6&9\\5&8&11\end{pmatrix} 1354687911

上个月,马克西姆构建了一个前进方阵,并记住了 n n n c c c d d d的值。最近,他发现了一个由 n 2 n^2 n2个整数随机排列而成的数组 b b b,并希望确保这些元素就是该特定正方形的元素。

可以证明,对于 n n n a 1 , 1 a_{1,1} a1,1 c c c d d d中的任意值,都存在一个满足所有规则的渐进方格。

分析:

记录给定矩阵中每个数出现次数,选择其中最小值为 a 1 , 1 a_{1,1} a1,1,构造出合法矩阵的唯一形态并检查每个数出现次数是否合法即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
map<LL, int> cnt;
vector<int> b;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, c, d;
        cin >> n >> c >> d;
        cnt.clear();
        b.clear();
        for (int i = 1; i <= n * n; i++) {
            int x;
            cin >> x;
            cnt[x] = cnt[x] + 1;
            b.push_back(x);
        }
        sort(b.begin(), b.end());
        int a11 = b[0], flag = 1;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                LL x = a11 + 1ll * (i - 1) * c + 1ll * (j - 1) * d;
                if (!cnt.count(x) || cnt[x] == 0) flag = 0;
                cnt[x] = cnt[x] - 1;
            }
        }
        cout << (flag ? "YES" : "NO") << endl;
    }
    return 0;
}

C.Inhabitant of the Deep Sea(模拟)

题意:

n n n艘飞船出发探索海洋深处。这些飞船的编号从 1 1 1 n n n,依次递增;第 i i i艘飞船的耐久度为 a i a_i ai

克拉肯按照特定顺序攻击了船只 k k k次。首先,它攻击第一艘飞船,然后是最后一艘,接着又是第一艘,以此类推。

克拉肯的每次攻击都会使飞船的耐久度降低 1 1 1。当船只的耐久度下降到 0 0 0时,它就会沉没,不再受到攻击(因此船只不再是第一艘或最后一艘,克拉肯只攻击尚未沉没的船只)。如果所有的船只都沉没了,克拉肯也就没有什么可攻击的了,于是它就游走了。

例如,如果 n = 4 n=4 n=4 k = 5 k=5 k=5 a = [ 1 , 2 , 4 , 3 ] a=[1,2,4,3] a=[1,2,4,3],就会发生以下情况:

  1. 克拉肯攻击第一艘飞船,其耐久度变为零,现在为 a = [ 2 , 4 , 3 ] a=[2,4,3] a=[2,4,3]
  2. 卡拉基攻击最后一艘飞船,现在为 a = [ 2 , 4 , 2 ] a=[2,4,2] a=[2,4,2]
  3. 克拉肯攻击第一艘飞船,现在为 a = [ 1 , 4 , 2 ] a=[1,4,2] a=[1,4,2]
  4. 克拉肯号攻击最后一艘飞船,现在是 a = [ 1 , 4 , 1 ] a=[1,4,1] a=[1,4,1]
  5. 克拉肯攻击第一艘飞船,其耐久度变为零,现在为 a = [ 4 , 1 ] a=[4,1] a=[4,1]

克拉肯攻击后有多少艘船被击沉?

分析:

k k k分解成两部分,然后两边往中间暴力枚举删数,即每次将左右两端血量较少的一个消灭即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 1e6 + 7;

LL t, n, k, a[N];

void solve() {
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    LL l = (k + 2 - 1) / 2;
    LL r = k / 2;
    LL rt = 1;
    LL ans = 0;
    while (l != 0) {
        if (rt > n) {
            break;
        }
        if (l - a[rt] >= 0) {
            l -= a[rt];
            ans++;
            a[rt] = 0;
            rt++;
        } else {
            a[rt] -= l;
            l = 0;
            break;
        }
    }
    rt = n;
    while (r != 0) {
        if (rt < 1) {
            break;
        }
        if (a[rt] == 0) {
            break;
        }
        if (r - a[rt] >= 0) {
            r -= a[rt];
            ans++;
            a[rt] = 0;
            rt--;
        } else {
            a[rt] -= r;
            r = 0;
            break;
        }
    }
    cout << ans << endl;
}

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

D.Inaccurate Subsequence Search(双指针)

题意:

马克西姆有一个由 n n n个整数组成的数组 a a a和一个由 m m m个整数组成的数组 b b b m ≤ n m\le n mn)。

如果数组 c c c中的元素可以重新排列,使其中至少 k k k个元素与数组 b b b中的元素匹配,那么他认为长度为 m m m的数组 c c c是好数组。

例如,如果 b = [ 1 , 2 , 3 , 4 ] b=[1,2,3,4] b=[1,2,3,4] k = 3 k=3 k=3,那么数组 [ 4 , 1 , 2 , 3 ] [4,1,2,3] [4,1,2,3] [ 2 , 3 , 4 , 5 ] [2,3,4,5] [2,3,4,5]就是好数组(它们可以重新排列如下: [ 1 , 2 , 3 , 4 ] [1,2,3,4] [1,2,3,4] [ 5 , 2 , 3 , 4 ] [5,2,3,4] [5,2,3,4]),而数组 [ 3 , 4 , 5 , 6 ] [3,4,5,6] [3,4,5,6] [ 3 , 4 , 3 , 4 ] [3,4,3,4] [3,4,3,4]则不是好数组。

马克西姆希望选择数组 a a a长度为 m m m的每个子段作为数组 c c c的元素。帮助他计算有多少个子段是好的。

换句话说,找出有多少个位置 1 ≤ l ≤ n − m + 1 1\le l\le n-m+1 1lnm+1的元素 a l , a l + 1 , … , a l + m − 1 a_l,a_{l+1},\dots, a_{l+m-1} al,al+1,,al+m1构成了一个好数组。

分析:

本题为滑动窗口,考虑使用双指针,用一个双指针在 a a a数组中维护一个长度为 m m m区间,同时维护当中与 b b b数组中匹配的数量 c n t cnt cnt,使用遍历进行维护肯定不行,但可以发现一个性质,就是随着区间移动,他只有首位的数字发生变化,所以我们只需要判断首位的数字匹配情况,来更新 c n t cnt cnt即可。

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 2e5 + 5;

int arr[N], brr[N];

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; i++) {
        cin >> arr[i];
    }
    map<int, int> mp;
    for (int i = 1; i <= m; i++) {
        cin >> brr[i];
        mp[brr[i]]++;
    }
    map<int, int> temp;

    int res = 0;
    int cnt = 0;

    for (int r = 1, l = 1; r <= n; r++) {
        temp[arr[r]]++;
        if (temp[arr[r]] <= mp[arr[r]])
            cnt++;
        if (r > m) {
            temp[arr[l]]--;
            if (temp[arr[l]] < mp[arr[l]])
                cnt--;
            l++;
        }
        if (r >= m && cnt >= k)
            res++;
    }
    cout << res << endl;
}


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

E.Long Inversions(差分)

题意:

给出长度为 n n n的二进制字符串 s s s。二进制字符串是仅由字符"1"和"0"组成的字符串。

可以选择一个整数 k k k( 1 ≤ k ≤ n 1\le k\le n 1kn),然后任意多次进行以下运算:选择字符串中连续的 k k k个字符并将其反转,即用"1"替换所有"0",反之亦然。

通过这些操作,您需要使字符串中的所有字符都等于"1"。

例如,如果是 n = 5 n=5 n=5 s = 00100 s=00100 s=00100,则可以选择 k = 3 k=3 k=3,并按以下步骤操作:

  • 选择从第 1 1 1到第 3 3 3个字符的子串,得到 s = 11000 s=\color{blue}{110}00 s=11000
  • 选择从第 3 3 3到第 5 5 5个字符的子串,得到 s = 11 111 s=11\color{blue}{111} s=11111

k k k的最大值,在这个值上,可以通过所述的操作使字符串中的所有字符都等于"1"。请注意,实现这一目标所需的操作次数并不重要。

分析:

从左到右贪心,例如 1011101 1011101 1011101这个样例,设 k = 4 k=4 k=4,如果只对某一位取反,必然会影响到第 i + k i+k i+k位的数,在 k k k等于 4 4 4时,是否该取反的状态如下:

1 1 1到第 4 4 4个数: 0100 0100 0100

5 5 5到第 7 7 7个数: 010 010 010

此时按位异或,会发现他们均为 0 0 0

对样例 0100010 0100010 0100010,取 k = 4 k=4 k=4时,取反状态如下:
[ 1   4 ] : 1011 [1~4]:1011 [1 4]1011 [ 5   7 ] : 101 [5~7]:101 [5 7]101

按位异或不全为 0 0 0,故 k k k不可能取 4 4 4;取 k = 3 k=3 k=3时,有:

[ 1   3 ] : 101 [1~3]:101 [1 3]101 [ 4   6 ] : 110 [4~6]:110 [4 6]110 [ 7   7 ] : 1 [7~7]:1 [7 7]1

按位异或全为 1 1 1,其一种推导路径如下:

0100010 0100010 0100010 0111110 0111110 0111110 1001110 1001110 1001110 1001001 1001001 1001001 1110001 1110001 1110001 1111111 1111111 1111111

因此只需要开个差分数组,暴力从大到小枚举 k k k,找到使差分数组值全相等时的情况输出即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

LL k;
string s;

void solve() {
    cin >> k;
    cin >> s;
    do {
        vector<int> a(k);
        for (int i = 0; i <= s.length() - 1; i++)
            a[i % k] ^= '1' - s[i];
        if (a == vector<int>(k, a[0])) {
            cout << k << endl;
            return;
        }
    } while (k--);
}

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

F.Unfair Game(思维)

题意:

爱丽丝和鲍勃在傍晚时分聚集在一起,就一个由 n n n个整数组成的数列玩一个刺激的游戏,数列中的每个整数都不超过 4 4 4}。游戏规则太复杂,无法描述,所以我们只描述获胜条件–如果序列中所有数字的按位异或都非零,则爱丽丝获胜;否则,鲍勃获胜。

他们邀请夏娃担任裁判。一开始,爱丽丝和鲍勃用 n n n个数字进行游戏。一局游戏结束后,夏娃从序列中移除一个数字,然后爱丽丝和鲍勃用 n − 1 n-1 n1个数字进行游戏。夏娃再次删除一个数字,然后爱丽丝和鲍勃用 n − 2 n-2 n2个数字进行游戏。这个过程一直持续到数字序列为空为止。

夏娃似乎认为在这样的游戏中,爱丽丝几乎总是赢,所以她希望鲍勃赢的次数越多越好。如果夏娃以最佳方式移除数字,求鲍勃能赢爱丽丝的最大次数。

分析:

这个题其实很好想,首先可以想到 4 4 4只要有那么就一定和 1 , 2 , 3 1,2,3 1,2,3没有任何关系,因为无法组成 0 0 0,那么就将 4 4 4的数量单独拿出来看,再看 1 , 2 , 3 1,2,3 1,2,3的数量,可以想到 1 1 1 2 2 2可以和 3 3 3抵消掉,然后就可以想出一种方法,就是将 1 , 2 , 3 1,2,3 1,2,3的数量取 m i n min min,然后加上 4 4 4的数量除以 2 2 2,是一种情况,再看如果说每个的数字是偶数的话,那么很明显比 1 , 2 , 3 1,2,3 1,2,3抵消要更优,因为每个数字是偶数,如果说是第一种情况最多是 2 2 2,但是第二种情况每次减 2 2 2的话答案可以增加 3 3 3。最后就是特殊的情况,比如 1 , 2 , 3 1,2,3 1,2,3的数量一样,并且全是奇数,那么答案要加 1 1 1。如果说三个数字都是奇数,那么答案也要加 1 1 1

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

void solve() {
    LL a, b, c, d;
    cin >> a >> b >> c >> d;
    bool f = 0;
    if ((a == b && b == c && a % 2 == 1) || ((a % 2 == 1) && (b % 2 == 1) && (c % 2 == 1))) {
        f = 1;
    }
    LL res = (min(min(a, b), c) + d / 2);
    LL Res = (a / 2) + (b / 2) + (c / 2) + (d / 2) + ((f == 1) ? 1 : 0);
    cout << max(res, Res) << endl;
}

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

赛后交流

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值