ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1567A-E

2021-09-06 21:58:02  阅读:144  来源: 互联网

标签:10 int tr mx 区间 MX CF1567A


B. MEXor Mixup

有一未知数组的mex值为a,异或值为b,求数组的最小长度。

由于mex值为a,因此至少需要 [0,a-1] 这a个元素。令前a个元素的异或值为x,

  • 若x=b,则只需要前a个元素
  • 若x!=b,显然存在一个数y满足x^y=b,但是需要判断y是否是a,即是否会破坏mex条件。

代码

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

const int N = 3e5 + 5;

int pre[N];
void solve()
{
    int a, b;
    scanf("%d%d", &a, &b);
    int res = 0;
    res = pre[a - 1];
    if (res == b)
        printf("%d\n", a);
    else if ((res ^ b) == a)
        printf("%d\n", a + 2);
    else
        printf("%d\n", a + 1);
}

int main()
{
    for (int i = 1; i < N; i++)
        pre[i] = pre[i - 1] ^ i;
    int t;
    scanf("%d", &t);
    while (t--)
        solve();
    return 0;
}

C. Carrying Conundrum

赛时被C卡到自闭。

Solution I

奇数位进位到奇数位,偶数位进位到偶数位,因此可以将奇偶位分开来考虑,然后用乘法原理计算贡献。

设一个数为ABCDEF,拆成ACE和BDF两个数,单独考虑ACE,求a+b=ACE的方案数,显然方案数为ACE+1。同理 BDF的方案数为BDF+1,答案就是 ( A C E + 1 ) × ( B D F + 1 ) (ACE+1)\times (BDF+1) (ACE+1)×(BDF+1)再减去a为0,b为0 两种情况

代码

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

const int N = 1e5 + 5;

void solve()
{
    string a;
    cin >> a;
    int s[2], p = 0;
    s[0] = s[1] = 0;
    for (auto it : a)
    {
        s[p] = s[p] * 10 + it - '0';
        p ^= 1;
    }
    cout << (s[0] + 1) * (s[1] + 1) - 2 << endl;
}
int main()
{
    int t;
    cin >> t;
    while (t--)
        solve();

    return 0;
}

Solution II

可以考虑枚举进位的情况。

代码

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

int num[22];
void solve()
{

    string n;
    cin >> n;
    vector<int> vec(12);
    int len = n.size();
    for (int i = 0; i < len; i++)
        vec[10 - (len - i - 1)] = n[i] - '0';
    int ans = 0;
    for (int i = 0; i < (1 << 10); i++)
    {
        if (i % 4)
            continue;
        vector<int> temp = vec;
        bool flag = 1;
        int res = 1;
        for (int j = 0; j < 10; j++)
        {
            int p = 10 - j;
            if ((1 << j) & i)
                temp[p]--;
            if ((1 << (j + 2)) & i)
                temp[p] += 10;
            res *= num[temp[p]];
        }
        if (flag)
            ans += res;
    }
    printf("%d\n", ans - 2);
}

int main()
{
    for (int a = 0; a <= 9; a++)
        for (int b = 0; b <= 9; b++)
            for (int c = 0; c <= 18; c++)
                if (a + b == c)
                    num[c]++;
    int t;
    scanf("%d", &t);
    while (t--)
        solve();
    return 0;
}

D. Carrying Conundrum

对比一下十进制和十一进制

100 10 1
121 11 1

结论:将数放在尽可能的高位上是最优的。

但是又要满足所有数都不为0,因此贪心的将低位的数进行拆分,这样损失的贡献最少。

代码

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

const int N = 1e2 + 5;

int a[N];
int p10[N];

void solve()
{
    int s, n;
    scanf("%d %d", &s, &n);
    for (int i = 1; i <= n; i++)
        a[i] = 0;
    int num[100];
    int p = 0;
    while (s)
    {
        num[++p] = s % 10;
        s /= 10;
    }
    while (1)
    {
        int sum = 0;
        for (int i = 1; i <= p; i++)
            sum += num[i];
        if (sum < n)
        {
            for (int i = 2; i <= p; i++)
            {
                if (num[i])
                {
                    num[i]--;
                    num[i - 1] += 10;
                    break;
                }
            }
        }
        else
            break;
    }
    int pos = 1;

    for (int j = 1; j <= p; j++)
    {
        while (num[j]--)
        {
            a[pos++] += p10[j];
            if (pos > n)
                pos = 1;
        }
    }
    for (int i = 1; i <= n; i++)
        printf("%d ", a[i]);
    printf("\n");
}

void init()
{
    p10[1] = 1;
    for (int i = 2; i <= 10; i++)
        p10[i] = p10[i - 1] * 10;
}
int main()
{
    init();
    int t;
    scanf("%d", &t);
    while (t--)
        solve();

    return 0;
}

E. Carrying Conundrum

比较显然的线段树维护。

考虑合并两个区间时,怎么维护询问要求的 区间的贡献。

对于最底层、长度为1的区间(单个元素),显然贡献是1,合并两个长度为1的区间,贡献即是两个子区间的贡献和,再加上区间合并带来的新增贡献。只需维护好新增的贡献即可。

考虑维护新增贡献。

假设左区间为1 2 3 1 2 3 。右区间为4 5 1 2。
记左区间的最长的以右端点结束的不下降区间长度为 M X R MX_R MXR​
记右区间的最长的以左端点开始的不下降区间长度为 M X L MX_L MXL​
当左区间的右端点值小于右区间的左端点值时,显然新增贡献为 M X R × M X L MX_R\times MX_L MXR​×MXL​。

那么对于每个区间,还需要维护好MX_L、MX_R。

考虑维护MX_L。(MX_R同理)

还是以上面两个区间为例,现有:

LSON.MX_L=3
LSON.MX_R=3
RSON.MX_R=2
RSON.MX_R=2

要求

FATHER.MX_L
FATHER.MX_R

这个条件总是成立:

FATHER.MX_L=LSON.MX_L
FATHER.MX_R=RSON.MX_R

那么FATHER.MX_L (/FATHER.MX_R)能否进行拓展呢?

当左区间的右端点值小于右区间的左端点值、且LSON.MX_L为LSON的整个区间长度时,才可以进行拓展。R同理。

另外,询问时需要注意的问题:假设询问区间为[5,6],可能在线段树上询问的是LSON[1,5],RSON[6,10],这时候LSON.MX_R和RSON.MX_L就需要和询问区间取一个min

代码:

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

const int N = 2e5 + 5;

int a[N];

struct node
{
    int k, l, r, mx_L, mx_R;
    long long sum;
    //mx_L 从左端点开始 最长的不下降子区间长度
    //mx_R 以右端点结束 最长的不下降子区间长度
} tr[N << 2];

void push_up(int k)
{
    long long res = 0;
    tr[k].mx_L = tr[k << 1].mx_L;
    tr[k].mx_R = tr[k << 1 | 1].mx_R;
    if (a[tr[k << 1].r] <= a[tr[k << 1 | 1].l]) //考虑连接两个区间
    {
        res = 1ll * (tr[k << 1].mx_R) * (tr[k << 1 | 1].mx_L);
        if (tr[k << 1].mx_L == tr[k << 1].r - tr[k << 1].l + 1) //如果整个左儿子区间都是不下降的,那么可以合并
            tr[k].mx_L = tr[k << 1 | 1].mx_L + (tr[k << 1].r - tr[k << 1].l + 1);

        if (tr[k << 1 | 1].mx_R == tr[k << 1 | 1].r - tr[k << 1 | 1].l + 1) //如果整个右儿子区间都是不下降的,那么可以合并
            tr[k].mx_R = tr[k << 1].mx_R + (tr[k << 1 | 1].r - tr[k << 1 | 1].l + 1);
    }
    tr[k].sum = tr[k << 1 | 1].sum + tr[k << 1].sum + res;
}

void build(int k, int l, int r)
{
    tr[k].l = l, tr[k].r = r;
    if (l == r)
    {
        tr[k].sum = tr[k].mx_L = tr[k].mx_R = 1;
        return;
    }
    int mid = l + r >> 1;
    build(k << 1, l, mid);
    build(k << 1 | 1, mid + 1, r);
    push_up(k);
}
void change(int k, int pos, int val)
{
    if (tr[k].l == tr[k].r)
    {
        tr[k].sum = tr[k].mx_L = tr[k].mx_R = 1;
        return;
    }
    int mid = tr[k].l + tr[k].r >> 1;
    if (pos <= mid)
        change(k << 1, pos, val);
    else
        change(k << 1 | 1, pos, val);
    push_up(k);
}
long long query(int k, int l, int r)
{
    if (tr[k].l == l && tr[k].r == r)
        return tr[k].sum;
    int mid = tr[k].l + tr[k].r >> 1;
    long long ans = 0;
    if (r <= mid)
        ans += query(k << 1, l, r);
    else if (l > mid)
        ans += query(k << 1 | 1, l, r);
    else
    {
        ans += query(k << 1, l, mid);
        ans += query(k << 1 | 1, mid + 1, r);

        if (a[tr[k << 1].r] <= a[tr[k << 1 | 1].l]) //中间的贡献,注意要和区间大小取min。
            ans += 1ll * min(mid - l + 1, tr[k << 1].mx_R) *
                   min(r - (mid + 1) + 1, tr[k << 1 | 1].mx_L);
    }
    return ans;
}

void solve()
{
    int n, q;
    scanf("%d %d", &n, &q);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    build(1, 1, n);
    while (q--)
    {
        int opt, l, r;
        scanf("%d %d %d", &opt, &l, &r);
        if (opt == 1)
        {
            a[l] = r;
            change(1, l, r);
        }
        else
            printf("%lld\n", query(1, l, r));
    }
}

int main()
{
    solve();
    return 0;
}

标签:10,int,tr,mx,区间,MX,CF1567A
来源: https://blog.csdn.net/hesorchen/article/details/120145831

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有