Codeforces Round 929(div3)||ABCDE

文章介绍了四个编程问题,涉及数组排序、取反操作、使数组和为3的倍数的最少操作、非负整数的计数、数组模运算后的最大和、以及寻找最优训练策略。每个问题都需要利用特定的算法技巧来解决。

A-Turtle Puzzle: Rearrange and Negate

题意

对一个数组执行两个操作:

  1. 对数组进行重新排序或保持元素顺序不变
  2. 选择连续的一段,对该段中的元素取相反数,也可以不选择任何一段,即保持所有的元素符号不变。

求进行上述操作之后数组的最大和是多少。

数据范围

t(1≤t≤1000)t(1≤t≤1000)t(1t1000)

n(1≤n≤50)n(1≤n≤50)n(1n50)

ai(−100≤ai≤100)a_i(-100\le a_i\le 100)ai(100ai100)

思路

遍历数组,对所有的数取非负后相加。

参考代码

void solve() {
    int n;cin >> n;
    ll ans = 0ll;
    for (int i = 0;i < n;i++) {
        ll x;cin >> x;
        if (x < 0)ans -= x;
        else ans += x;
    }
    cout << ans << '\n';
}

B-Turtle Math: Fast Three Task

题意

有一个数组,可以对数组中的数进行任意次下方两种操作:

  1. 将数移除
  2. 将该数的数值加1

求至少进行多少次上述操作,可以使数组所有元素之和是3的倍数?

数据范围

t(1≤t≤104)t(1≤t≤10^4)t(1t104)

n(1≤n≤105)n(1≤n≤10^5)n(1n105)

ai(1≤ai≤104)a_i(1\le a_i\le 10^4)ai(1ai104)

思路

统计数组aaa中模3为0、1、2的数量和余数总和。记总和为sumsumsum,余1的数量为xxx,余2的数量为yyy。考虑:

  1. sumsumsum模3为0,则不需要操作
  2. sumsumsum模3为2,则给任意一个数加1即可,操作1次。
  3. sumsumsum模3为1,若有余1的数,则去掉这个数即可,否则进行两次加1操作。

参考代码

void solve() {
    int n;cin >> n;
    vector<ll>aa(n);
    ll ans = 0;
    ll x = 0, y = 0;
    for (int i = 0;i < n;i++) {
        cin >> aa[i];
        aa[i] %= 3;
        ans += aa[i];
        if (aa[i] == 1)x++;
        else if (aa[i] == 2)y++;
    }
    if (ans%3 == 0) {
        cout<<0<<'\n';
    }
    else if (ans % 3 == 2) {
        cout << 1 << '\n';
    }
    else {
        if (x > 0)cout << 1 << '\n';
        else cout << 2 << '\n';
    }
}

C-Turtle Fingers: Count the Values of k

题意

给3个正整数a,b,la,b,la,b,l,找出满足l=k×ax×byl=k\times a^x\times b^yl=k×ax×bykkk的个数,k,x,yk,x,yk,x,y均为非负整数。

数据范围

t(1≤t≤104)t(1≤t≤10^4)t(1t104)

a,b,l(2≤a,b≤100,1≤l≤106)a,b,l(2\le a,b\le 100,1\le l\le 10^6)a,b,l(2a,b100,1l106)

思路

220>1062^{20}\gt 10^6220>106,可知,x,yx,yx,y的范围不超过20。

预处理axa^xaxbyb^yby,然后暴力遍历即可。

参考代码

void solve() {
    ll a, b, l;cin >> a >> b >> l;
    vector<ll>ax, by;
    ax.push_back(1);by.push_back(1);
    for (int i = 1;ax.back() <= l;i++) {
        ax.push_back(ax.back() * a);
    }
    for (int i = 1;by.back() <= l;i++) {
        by.push_back(by.back() * b);
    }
    set<ll>k;
    for (int i = 0;i < ax.size();i++) {
        for (int j = 0;j < by.size();j++) {
            if (l%(ax[i] * by[j]) == 0) {
                k.insert(l/(ax[i] * by[j]));
            }
        }
    }
    cout << k.size() << '\n';
}

D-Turtle Tenacity: Continual Mods

题意

给数组aaa重新排序,判断是否存在排序使得a1 mod a2 mod a3…an−1 mod an=0a_1 \text{ }mod\text{ } a_2 \text{ }mod\text{ } a_3\dots a_{n-1}\text{ }mod\text{ }a_n=0a1 mod a2 mod a3an1 mod an=0

数据范围

t(1≤t≤104)t(1≤t≤10^4)t(1t104)

n(2≤n≤105)n(2≤n≤10^5)n(2n105)

ai(1≤ai≤109)a_i(1\le a_i\le 10^9)ai(1ai109)

思路

思考x mod yx\text{ }mod\text{ }yx mod y

  1. 如果x<yx\lt yx<y,则结果还是xxx
  2. 如果x=yx=yx=y​,则结果是0

如果最小的数是唯一的,则一定有解;如果最小的数不唯一,考虑是否有较大的数zzz使得z mod x≠0z\text{ }mod\text{ }x≠0z mod x=0,如果存在,则有更小的唯一最小值,可以有解,否则无解。

参考代码

void solve() {
    int n;cin >> n;
    vector<ll>a(n);
    for (int i = 0;i < n;i++) {
        cin >> a[i];
    }
    sort(a.begin(), a.end());
    if (a[0] != a[1]) {
        cout << "YES\n";
        return;
    }
    for (int i = 1;i < n;i++) {
        if (a[i] % a[0] != 0) {
            cout << "YES\n";
            return;
        }
    }
    cout << "NO\n";
}

E-Turtle vs. Rabbit Race: Optimal Trainings

题意

训练量kkk是连续一段时间的每天的训练量的总和,每次训练的提高值uuu按照训练次数递减(第1次uuu,第2次u−1u-1u1,第3次u−2u-2u2,…,第kkku−k+1u-k+1uk+1,,提高值可以是负数),每次给定一个起始日lll和提高值uuu,寻找一个最佳的结束日rrr,使得训练提高值总和最高,如果有多个rrr的结果提供最高训练值,选rrr较小的那个。

数据范围

t(1≤t≤104)t(1≤t≤10^4)t(1t104)

n(1≤n≤105)n(1≤n≤10^5)n(1n105)

ai(1≤ai≤104)a_i(1\le a_i\le 10^4)ai(1ai104)

q(1≤q≤105)q(1\le q\le 10^5)q(1q105)

l,u(1≤l≤n,1≤u≤109)l,u(1\le l\le n,1\le u \le 10^9)l,u(1ln,1u109)

思路

训练提高值总量SSS与训练量kkk之间的关系是S(k)=u×k−k×(k−1)2S(k)=u\times k-\frac{k\times (k-1)}{2}S(k)=u×k2k×(k1),是一个关于kkk先增后减的函数,最高值在k=u+0.5k=u+0.5k=u+0.5处取到,由于kkk为整数,S(k)S(k)S(k)的最高值应该在uuuu+1u+1u+1处取到。

  1. 在对称轴左边,二分查找在[l,u][l,u][l,u]的范围内最靠近uuukkk的取值,即小于等于uuu的最后一个kkk值。
  2. 在对称轴右边,二分查找[u+1,n][u+1,n][u+1,n]的范围内最靠近u+1u+1u+1kkk值,即大于等于u+1u+1u+1的第一个kkk值。

kkk值可以通过前缀和进行筛选,k=pre[r]−pre[l−1]k=pre[r]-pre[l-1]k=pre[r]pre[l1],则对preprepre数组进行二分查找u+pre[l−1]u+pre[l-1]u+pre[l1]u+1+pre[l−1]u+1+pre[l-1]u+1+pre[l1]即可。

对比这两个值对应的S(k)S(k)S(k)rrr,以及只在lll那天训练的效果,择优选择。

参考代码

ll f(ll u, ll k) {
    return k * u - k * (k - 1) / 2;
}

void solve() {
    int n;cin >> n;
    vector<ll>a(n + 1);
    vector<ll>pre(n + 1);
    pre[0] = 0;a[0] = 0;
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        pre[i] = pre[i - 1] + a[i];
    }
    int q;cin >> q;
    while (q--) {
        ll l, u;cin >> l >> u;
        ll x = pre[l - 1];
        int ru = upper_bound(pre.begin() + l, pre.end(), x + u) - pre.begin() - 1;
        ll ans = a[l], ansr = l;
        if (ru >= l && ru <= n) {
            if (f(u, pre[ru] - x) > ans) {
                ans = f(u, pre[ru] - x);
                ansr = ru;
            }
        }
        int ru1 = lower_bound(pre.begin() + l, pre.end(), x + u + 1) - pre.begin();
        if (ru1 >= l, ru1 <= n) {
            if (f(u, pre[ru1] - x) > ans) {
                ans = f(u, pre[ru1] - x);
                ansr = ru1;
            }
        }
        cout << ansr << ' ';
    }
    cout << '\n';
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值