Codeforces Round #742 (Div. 2) 题解

8 篇文章 0 订阅
8 篇文章 0 订阅

旅行传送门

A. Domino Disaster

题意:有一个被大小为 1 ∗ 2 1*2 12 的多米诺骨牌覆盖了的 2 2 2 n n n 列的网格。 骨牌可以垂直或水平放置,告诉你其中一排网格,问另一排网格是何样?

题目分析:模拟即可。

AC代码

#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
using namespace std;

void solve()
{
    int n;
    cin >> n;
    string s, t;
    cin >> s;
    rep(i, 0, n - 1)
    {
        switch (s[i])
        {
        case 'U':
            t += 'D';
            break;
        case 'D':
            t += 'U';
            break;
        case 'L':
            t += 'L';
            break;
        case 'R':
            t += 'R';
            break;
        default:
            break;
        }
    }
    cout << t << endl;
}

int main(int argc, char const *argv[])
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}

B. MEXor Mixup

题意:给你两个数 a a a b b b ,其中 a a a 是数组中最小的未出现的非负整数, b b b 是整个数组的异或和,问满足条件的数组的最小长度是多少。

题目分析:首先可以明确的是所求 a n s ≥ a ans \geq a ansa ,不妨设 c c c 0 0 0 ~ a − 1 a-1 a1 的异或和,接下来分三种情况讨论即可:

  • c = b c = b c=b ,此时 a n s = a ans = a ans=a

  • c ≠ b c \not= b c=b c ⨁ b ≠ a c \bigoplus b \not= a cb=a ,此时 a n s = a + 1 ans = a+1 ans=a+1

  • c ≠ b c \not= b c=b c ⨁ b = a c \bigoplus b = a cb=a ,此时 a n s = a + 2 ans = a+2 ans=a+2

这里解释一下,由于 b b b 是整个数组的异或和, 现在我们要让 c c c 异或上某个值 d d d 得到 b b b ,如果 d d d 不是 a a a 的话,我们直接把 d d d 添加进数组就好,否则的话就要用另外两个值异或起来得到 a a a 再去和 c c c 异或得到 b b b ,因为 a a a 不能出现在数组中,这种情况下就是 a n s = a + 2 ans = a+2 ans=a+2

有个异或和的小性质: i i i 0 0 0 开始算起, 有 i ⨁ i + 1 = 1 i \bigoplus i+1 = 1 ii+1=1

AC代码

#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
const int maxn = 3e5 + 5;

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf; 
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

int main(int argc, char const *argv[])
{
    int T = read();
    std ::vector<int> c(maxn);
    rep(i, 1, 300000) c[i] = c[i - 1] ^ i;
    while (T--)
    {
        int a = read(), b = read();
        if (c[a - 1] == b)
            printf("%d\n", a);
        else if (c[a - 1] ^ b ^ a)
            printf("%d\n", a + 1);
        else
            printf("%d\n", a + 2);
    }
    return 0;
}

// int check(int a)
// {
//     switch (a % 4)
//     {
//     case 0:
//         return 0;
//     case 1:
//         return a - 1;
//     case 2:
//         return 1;
//     case 3:
//         return (a - 1) ^ 1;
//     }
// }

// int main(int argc, char const *argv[])
// {
//     int T = read();
//     while (T--)
//     {
//         int a = read(), b = read();
//         int c = check(a);
//         if (c == b)
//             printf("%d\n", a);
//         else if (c ^ b ^ a)
//             printf("%d\n", a + 1);
//         else
//             printf("%d\n", a + 2);
//     }
//     return 0;
// }

C. Carrying Conundrum

题意:定义新的加法运算:计算时所有的进位都要左移一位。

给你一个数 n n n ,问有多少对 ( a , b ) (a,b) (a,b) 在新定义下满足 a + b = n a + b = n a+b=n ( a , b ) (a,b) (a,b) ( b , a ) (b,a) (b,a) 算作不同方案)?

题目分析:平时的加法运算进位规则是个位进十位、十位进百位、百位进千位……每一位的进位都对它的后一位有贡献,密不可分。但在新定义下,贡献关系变为奇数位只对奇数位有贡献,偶数位只对偶数位有贡献,因此我们扫描整个 n n n ,将奇偶数位的数分别抽出来,称为 o d d odd odd e v e n even even ,一个数 x x x 可以由 ( 0 , x ) (0,x) (0,x) ( 1 , x − 1 ) (1,x-1) (1,x1) ( 2 , x − 2 ) (2,x-2) (2,x2) …… ( x − 1 , 1 ) (x-1,1) (x1,1) ( x , 0 ) (x,0) (x,0) x + 1 x+1 x+1 种方案组成,所以答案即为 ( o d d + 1 ) ∗ ( e v e n + 1 ) (odd+1)*(even+1) (odd+1)(even+1) ,但我们还要减去 2 2 2 ,因为 ( a , b ) (a,b) (a,b) 为正整数对。因此,答案是 ( o d d + 1 ) ∗ ( e v e n + 1 ) − 2 (odd+1)*(even+1)-2 (odd+1)(even+1)2

举个栗子: 2021 2021 2021 , 奇数位与偶数位组成的数字分别为 22 22 22 01 01 01 22 22 22 的组成方式有 23 23 23 种, 1 1 1 的话有 2 2 2 种,但 ( 0 , 2021 ) (0,2021) (0,2021) ( 2021 , 0 ) (2021,0) (2021,0) 不符合题意,所以答案为 23 ∗ 2 − 2 = 44 23*2-2=44 2322=44 种。

AC代码

#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
using namespace std;

int main(int argc, char const *argv[])
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while (T--)
    {
        string s, odd, even;
        cin >> s;
        int len = s.length() - 1;
        rep(i, 0, len) if (i & 1) even += s[i];
        else odd += s[i];
        if (even.empty())
        {
            printf("%lld\n", stoi(odd) - 1);
            continue;
        }
        int a = stoi(odd), b = stoi(even);
        printf("%lld\n", 1ll * (a + 1) * (b + 1) - 2);
    }
    return 0;
}

D. Expression Evaluation Error

题意:给你一个十进制的数 s s s ,要将其拆成 n n n 个十进制数 ,使得这 n n n 个十进制数在十一进制的表示下总和最大,求任一拆分方案。

题目分析:首先考虑什么时候会有损失,将 10 10 10 拆成 1 1 1 9 9 9 ,其和即从 1 0 ( 11 ) 10_{(11)} 10(11) (十进制下为 11 11 11 )变为 1 ( 11 ) + 9 ( 11 ) 1_{(11)}+9_{(11)} 1(11)+9(11) (十进制下为 10 10 10 ),损失了 1 1 1 。将 100 100 100 拆成 1 1 1 99 99 99 ,其和即从 10 0 ( 11 ) 100_{(11)} 100(11) (十进制下为 121 121 121 )变为 1 ( 11 ) + 9 9 ( 11 ) 1_{(11)}+99_{(11)} 1(11)+99(11) (十进制下为 109 109 109 ),损失了 12 12 12 ,但如果拆成 10 10 10 90 90 90 ,其和变为 1 0 ( 11 ) + 9 0 ( 11 ) 10_{(11)}+90_{(11)} 10(11)+90(11) (十进制下为 110 110 110 ),损失了 11 11 11 ,较前者而言损失小了 1 1 1

观察上述结果,我们不难发现,每次产生损失都发生在将高位拆成低位的情况下,并且拆分后的位数越低,损失越大,说人话就是,将 100 100 100 拆成 1 1 1 99 99 99 ,就是将一个三位数拆分成了两位数和一位数,而如果拆成 10 10 10 90 90 90 ,就是将一个三位数拆分成了两个两位数。

所以我们要按位贪心,让拆分后的数最高位尽可能的大,即将 s s s 拆分为 m ∗ 1 0 n m*10^n m10n 之和,此时损失与 n n n 的大小成反比,但 m m m 是不会产生影响的, 100 100 100 拆成 10 10 10 90 90 90 20 20 20 80 80 80 等价。

剩下的就看代码吧。

AC代码

#include <bits/stdc++.h>
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

int main(int argc, char const *argv[])
{
    int t = read();
    while (t--)
    {
        int s = read(), n = read();
        int d = (int)log10(s);
        int p = pow(10, d);
        for (int i = n; i > 1; --i)
        {
            while (s - p < i - 1)
                p /= 10;
            printf("%d ", p);
            s -= p;
        }
        printf("%d\n", s);
    }
    return 0;
}

E. Non-Decreasing Dilemma

题意:已知一个数列,你需要进行下面两种操作:

  • a x a_x ax 的值修改为 y y y

  • 求出某区间连续非递减子序列的个数

题目分析:这题的难点在于线段树的区间合并操作,解释下代码中各变量的意义,对线段树中的每个节点,维护以下信息:

  • s u m sum sum :非递减子序列的个数
  • l v lv lv :左端点的值
  • r v rv rv :右端点的值
  • m x l mxl mxl :左端最长的连续非递减子序列长度
  • m x r mxr mxr :右端最长的连续非递减子序列长度
  • l e n len len :区间长度
  • f l a g flag flag :整段区间是否为连续非递减子序列

具体合并过程看代码吧,理解起来并不难。

AC代码

#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
using ll = long long;
const int maxn = 2e5 + 5;

char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

std::vector<ll> a(maxn);
struct node
{
    ll sum, lv, rv, mxl, mxr, len, flag;
} tree[maxn << 2];

node merge(node x, node y)
{
    node res;
    res.sum = x.sum + y.sum;
    res.lv = x.lv, res.rv = y.rv;
    res.len = x.len + y.len;
    res.flag = 0;
    if (x.rv <= y.lv)
        res.sum += x.mxr * y.mxl;
    if (x.flag && x.rv <= y.lv)
        res.mxl = x.len + y.mxl;
    else
        res.mxl = x.mxl;
    if (y.flag && x.rv <= y.lv)
        res.mxr = x.mxr + y.len;
    else
        res.mxr = y.mxr;
    if (x.flag && y.flag && x.rv <= y.lv)
        res.flag = 1;
    return res;
}

#define lson (k << 1)
#define rson (k << 1 | 1)
void build(ll k, ll l, ll r)
{
    if (l == r)
    {
        tree[k].lv = tree[k].rv = a[l];
        tree[k].sum = tree[k].mxl = tree[k].mxr = tree[k].len = tree[k].flag = 1;
        return;
    }
    ll mid = (l + r) >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    tree[k] = merge(tree[lson], tree[rson]);
}

void update(ll k, ll l, ll r, ll x, ll y)
{
    if (l == r && l == x)
    {
        tree[k].lv = tree[k].rv = y;
        return;
    }
    ll mid = (l + r) >> 1;
    x <= mid ? update(lson, l, mid, x, y) : update(rson, mid + 1, r, x, y);
    tree[k] = merge(tree[lson], tree[rson]);
}

node query(ll k, ll l, ll r, ll ql, ll qr)
{
    if (l >= ql && r <= qr)
        return tree[k];
    ll mid = (l + r) >> 1;
    if (qr <= mid)
        return query(lson, l, mid, ql, qr);
    else if (ql > mid)
        return query(rson, mid + 1, r, ql, qr);
    else
        return merge(query(lson, l, mid, ql, qr), query(rson, mid + 1, r, ql, qr));
}

int main(int argc, char const *argv[])
{
    int n = read(), q = read();
    rep(i, 1, n) a[i] = read();
    build(1, 1, n);
    while (q--)
    {
        int opt = read(), l = read(), r = read();
        if (opt == 1)
            update(1, 1, n, l, r);
        else
            printf("%lld\n", query(1, 1, n, l, r).sum);
    }
    return 0;
}

诉状罪书:昨天推gal把CF忘得一干二净,意识到的时候罪恶感已经爬上了脊背(照样睡到日上三竿),但是9-nine天天天推得真的停不下来(兄妹相声太好玩了),最后补一句:妹妹是天!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值