Codeforces Round #782 (Div. 2)

目录

A. Red Versus Blue

B. Bit Flipping

C. Line Empire

D. Reverse Sort Sum


A. Red Versus Blue

A. Red Versus Blue

题目翻译

输入三个数 n, r, k 别表示 总字符数,R字符数,B字符数,知,r > k ,输出一个字符串,是连续的R字符数量最小;

思路:将 r 分为 (k+1) 个部分,记录 a = r/(k+1) ,b = r%(k+1) 枚举即可,a + b 为一部分,若 b 没了,单独的 a 作为一部分

代码如下

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int N = 1e6+10, mod = 998244353;

int n, m, k;

void solve()
{
    string s;
    int n, m, k;
    cin >> n >> m >> k;

    int t = m/(k+1);
    int x = m%(k+1);

    //cout << t << endl;
    //cout << x << endl;

    int res = 0, t2 = 0;
    while( res < n)
    {
        t2 ++;
        for(int i = 0; i < t && res < n; i ++ )
            printf("R"), res ++;
        if(t2 < x) printf("R"), res ++;
        if(k > 0) printf("B"), k -- , res ++;
    }

    puts("");
    return ;
}

int main()
{
    cin >> n;

    while(n -- )
        solve();

    return 0;
}

B. Bit Flipping

B. Bit Flipping

题目翻译:

输入 n, k ;n 表示数组元素个数(数组元素为 0 或 1),k 表示可以反转的此数,每次反转必须选一个数据固定不动;然后,输入n 个数组元素,输出反转后的最大的数据

思路:

从前往后固定元素的值,使每个元素尽量为 1

因为每个元素都得反转,所以我们通过反转的此数的奇偶性和元素的01值,来确定如何使数组元素等于 1,

数组 f 表示固定每个元素的此数

有四种情况:

1) k 为奇数,a[i] 为 1,此时我们要想使 a[i] 为1,要固定一次 a[i],即,f[i] = 1

2)k 为奇数,a[i] 为 0 ,此时我们要想是 a[i] 为 1,不需要固定,即,f[i] = 0

3)k 为偶数,a[i] 为 1 ,此时我们要想是 a[i] 为 1,不需要固定,即,f[i] = 0

1) k 为偶数,a[i] 为 0,此时我们要想使 a[i] 为1,要固定一次 a[i],即,f[i] = 1

最后枚举每个元素的 反转次数 (k-f[i]) 的奇偶性判断是否令元素反转

代码如下:

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef pair<char, int> PCI;
typedef long long LL;

const int N = 2e5+10;

int T;

void solve()
{
    int n, k;
    scanf("%d %d", &n, &k);

    string s;
    int a[N], f[N] = {0};
    cin >> s;
    for(int i = 0; i < n; i ++ )
        a[i] = s[i]-'0';

    int t = k;
    for(int i = 0; i < n-1 && t > 0; i ++ )
    {
        if( k%2 == a[i])
        {
            f[i] = 1;
            t --;
        }
    }
    f[n-1] = t;

    for(int i = 0; i < n; i ++ )
    {
        if( (k-f[i])%2 ) a[i] = a[i]^1;
    }

    for(int i = 0; i < n; i ++ )
        cout << a[i];
    puts("");
    for(int i = 0; i < n; i ++ )
        cout << f[i] << " ";
    puts("");

    return;
}

int main()
{
    scanf("%d", &T);

    while(T -- )
        solve();

    return 0;
}

C. Line Empire

C. Line Empire

题目翻译:

背景为国王占领城堡;给定三个数 n, a, b 分别表示,要攻陷的城堡数,搬首都的每公里代价,直接打的每公里代价;

输入 n 个城堡的距离

思路:

贪心,先直接打(因为第一步没法迁都),再判断一下,迁都到该城堡的代价 和 直接打其他城堡的过程中经过此城堡的总代价,取较小的值,

代码如下:

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef pair<char, int> PCI;
typedef long long LL;

const int N = 2e5+10;

int T;

void solve()
{
    LL n, a, b, v[N] = {0};
    scanf("%lld %lld %lld", &n, &a, &b);

    for(int i = 1; i <= n; i ++ )
        scanf("%lld", &v[i]);
    int tmp = 0;

    LL res = 0;
    for(int i = 1; i <= n; i ++ )
    {
        // 不管三七二十一,直接打
        res += b*(v[i] - v[tmp]);
        // 判断最小代价
        if( a*(v[i] - v[tmp]) <= b*(v[i] - v[tmp])*(n-i))
        {
            res += a*(v[i]-v[tmp]);
            tmp = i;
        }
    }

    cout << res << endl;

    return;
}

int main()
{
    scanf("%d", &T);

    while(T -- )
        solve();

    return 0;
}

D. Reverse Sort Sum

D. Reverse Sort Sum

题目翻译

有一个包含 n 个元素的数组 A ,每个元素都是 0 或 1;

二维数组 B 每行分别为 A 将前 i 个元素升序排序后数组 A,即,B[1] = 升序排序了前 1 个元素的数组A,B[n] 为升序排序了前 n 个元素的数组A;

存在一维数组C,保存的是二维数组 B 的每一列的和;

已知数组C,求数组A

思路:树状数组 + 差分

举例:

红色为已排序部分

我们有题意可知三点线索:

(1):可求得原数组中 1 的个数:

        B数组的每一维都是 A 数组的数字一样,只是顺序不一样而已,所以 B 数组的所有元素求和等于 n 行的 A数组求和,求和的结果除以 n 得到,A数组中的 1个数,恰好C数组为B数组的每一列求和,所以C数组的和即为 n个A数组的和,将输入的C数组的值累加除以n即可得到A数组中的1 的个数。

(2):可根据最后一步排序 B[n] 来确定 A 数组的最后一步的数值,B[n] 为A数组全部元素升序排列的结果,若此时 B[1~n-1] 的第 n 个元素都为 1 ,即,无需排序第 n 为元素为 1 (等效于C[n] 等于 n )可得A数组的该位置元素确实为 1 ,若不全都是 1 ,那么可知 A数组该位置的元素为 0

(3):将第二部步骤向前递推,即,根据 B[n-1] 确定 A[n-1] ,B[n-2] 确定 A[n-2] ,以此类推。

具体向前递推过程

根据(2)计算数组A 的元素的前:1、该操作为最后一步操作;2、确定 C数组对应列的值

将原数组变换为满足条件 1 :

步骤为:将最后一列删去,将最后一行删去,更新其余的 C[i] ,计算即可

更新 C[i] 步骤为,减去 B[ n ](上次计算的一行) 的每一个数 ,我们可知,在B数组中的第 i 行已排序的元素的后 i 个元素必定为 1 (如果 k 有条件的话),具体例子解释:B数组的第四行的后三个元素为 1 ,第三行的二,三两个元素为 1 。

我们更新 C[i] 时仅需上一次计算的行的最后的几步 1 减去 k 个 1 即可(我们令此处计算一列 k 减去 1 ),举例说明,计算 B[3] 时,把 C[2] - 1,C[3] - 1,C[4] - 1(此处减1无意义,但为了理解加上去了,在后续的差分中也会加上去,会方便一点),注意,此处为区间修改操作,可用树状数组简化时间复杂度,达到O(nlogn),减去的区间范围为 [ i - k + 1, i ] ,此数区间长度恰好为 k ,根据B[i] 的升序排列的数据的最大索引 i ,推导出区间左端点为 i - k + 1,(当用树状数组+差分是就直接将 i - k + 1后面数据全部减去 1 ,大于 i 的数据已经没用,不会有任何影响)

区间修改可用树状数组 + 差分实现;

代码如下

(1)数组版(时间复杂度为O(n^2) TLE 版本,但有助于理解代码

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef pair<char, int> PCI;
typedef long long LL;

const int N = 2e5+10;

int T;
int n;
int tr[N];

int lowbit(int x)
{
    return x & -x;
}

void add(int x, int c)
{
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

int sum(int x)
{
    int res = 0;
    for(int i = x; i ; i -= lowbit(i)) res += tr[i];
    return res;
}

void solve()
{
    memset(tr, 0, sizeof tr);
    int a[N], b[N], c[N];
    scanf("%d", &n);

    LL x = 0;
    for(int i = 1; i <= n; i ++ )
    {
        scanf("%d", &c[i]);
        x += c[i];
    }

    x /= n;

    int res[N];
    for(int i = n; i ; i -- )
    {
        int l = i - x + 1;
        int t = c[i];
        if(t == i) a[i] = 1, x --;
        else a[i] = 0;
        for(int j = l; j <= n; j ++ )
            c[j] --;
    }

    for(int i = 1; i <= n; i ++ )
        printf("%d ", a[i]);
    puts("");

    return;
}

int main()
{
    scanf("%d", &T);

    while(T -- )
        solve();

    return 0;
}

(2)树状数组 + 差分(时间复杂度 O(nlogn) ),AC版本

#include <bits/stdc++.h>

using namespace std;

typedef pair<int, int> PII;
typedef pair<char, int> PCI;
typedef long long LL;

const int N = 2e5+10;

int T;
int n;
int tr[N];

int lowbit(int x)
{
    return x & -x;
}

void add(int x, int c)
{
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

int sum(int x)
{
    int res = 0;
    for(int i = x; i ; i -= lowbit(i)) res += tr[i];
    return res;
}

void solve()
{
    memset(tr, 0, sizeof tr);
    int a[N], b[N], c[N];
    scanf("%d", &n);

    LL x = 0;
    for(int i = 1; i <= n; i ++ )
    {
        scanf("%d", &c[i]);
        x += c[i];
        // 插入数据
        add(i, c[i]), add(i+1, -c[i]);
    }

    x /= n;

    int res[N];
    for(int i = n; i ; i -- )
    {
        int l = i - x + 1;
        int t = sum(i);
        if(t == i) a[i] = 1, x --;
        else a[i] = 0;
        // 区间修改
        add(l, -1);
    }

    for(int i = 1; i <= n; i ++ )
        printf("%d ", a[i]);
    puts("");

    return;
}

int main()
{
    scanf("%d", &T);

    while(T -- )
        solve();

    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AC自动寄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值