Codeforces Round 942 (Div.1) & (Div. 2) 2A~2D

2A.Contest Proposal(枚举)

题意:

一个竞赛包含nnn个问题,第iii个问题的难度预计最多bib_ibi。现在已经有nnn个问题提案,第iii个问题的难度为aia_iai。最初,a1,a2,…,ana_1,a_2,\ldots,a_na1,a2,,anb1,b2,…,bnb_1,b_2,\ldots,b_nb1,b2,,bn都是按非递减顺序排序的。

有些问题可能比预期的更难,因此作者必须提出更多的问题。当提出难度为www的新问题时,最难的问题将从竞赛中删除,问题将以难度不递减的方式排序。

换句话说,在每次操作中,你都要选择一个整数www,将其插入数组aaa中,对数组aaa进行非递减排序,并从中删除最后一个元素。

求使对于所有iii都有ai≤bia_i\le b_iaibi的的最小新问题数。

分析:

因为选的www一定可以让它合法,一次操作可以看作aaa数组向右平移一位。遍历aaa数组中每个数和第一个比bbb数组小的差值,取最大值即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 105;
LL a[N], b[N];

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n, ans = 0, flag = 0;
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        for (int i = 0; i < n; i++) {
            cin >> b[i];
        }
        for (int i = 0; i < n; i++) {
            flag = 0;
            for (int j = i; j < n; j++) {
                if (a[i] <= b[j]) {
                    ans = max(j - i, ans);
                    flag = 1;
                    j = n;
                }
            }
            if (flag == 0) {
                ans = max(n - i, ans);
            }
        }
        cout << ans << endl;
    }
    return 0;
}

2B.Coin Games(博弈)

题意:

桌子上有nnn枚硬币围成一个圆圈,每枚硬币要么朝上,要么朝下。爱丽丝和鲍勃轮流玩下面的游戏,爱丽丝先玩。

在每次操作中,玩家选择一枚正面朝上的硬币,取出硬币并翻转与其相邻的两枚硬币。如果(操作前)只剩下两枚硬币,则取出一枚,另一枚不翻转(因为会翻转两次)。如果(操作前)只剩下一枚硬币,则不会翻转任何硬币。如果(操作前)没有正面朝上的硬币,玩家就输了。

如果两人都以最佳方式下棋,谁会赢呢?可以证明,游戏将在有限次的操作中结束,其中一人将获胜。

分析:

UUU视作111,将DDD视作000,一次操作就是拿走一个111,将两侧分别异或111

观察样例,猜测UUU有奇数个先手赢,反之后手赢。

证明:根据上述转化,判定条件等价于判断全局异或和为000还是为111。一次操作对全局异或和的影响是将其与111进行异或。

初始异或和为111时,先手必定可以操作,到后手时异或和为000,后手可能可以操作,回到先手,异或和还是111,必定可以操作。如此往复,只要后手能操作,先手必定可以操作。

另一种情况类似。

代码:

#include<bits/stdc++.h>

using namespace std;

void solve() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    int cnt = 0;
    for (int i = 0; i < n; i++)
        if (s[i] == 'U')
            ++cnt;
    if (!cnt) {
        cout << "NO" << endl;
        return;
    }
    if (cnt & 1) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
}

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

2C1A.Permutation Counting(贪心)

题意:

你有一些卡片。每张卡片上都写着一个介于111nnn之间的整数:具体来说,从111nnn的每张iii,你们有aia_iai张卡片,上面写着数字iii

还有一个商店,里面有无限量的各种类型的卡片。你有kkk枚金币,因此你总共可以购买kkk张新卡片,你所购买的卡片可以包含111nnn之间的任意整数。

购买新牌后,您要将所有牌重新排列成一行。重新排列的得分是长度为nnn的(连续)子数组中[1,2,…,n][1,2,\ldots,n][1,2,,n]的排列数。你能得到的最高分是多少?

分析:

贪心地考虑优先用kkk买原本数量少的卡片,使每种卡片的最少数量尽可能大。

推导一下可以发现使答案尽可能大的结构是1,2,3,4,1,2,3,41,2,3,4,1,2,3,41,2,3,4,1,2,3,4这样的排列。

计算贡献,假设最小值为xxx,比xxx大的有yyy个,我们一定会把这yyy个尽量往前放,让较小的xxx利用充分,这样总共有(x−1)n+1+y(x−1)n+1+y(x1)n+1+y个合法子段。

然后就是模拟,把kkk尽量往数量少的分。使用差分维护即可。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;

void solve() {
    LL n, k;
    cin >> n >> k;
    vector<LL> a(n, 0);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    sort(a.begin(), a.end());
    vector<LL> dlt(n, 0);
    for (LL i = 0; i < n - 1; i++) {
        if (!k)
            break;
        LL x = (i + 1) * (a[i + 1] - a[i]);
        if (k >= x) {
            dlt[i] += a[i + 1] - a[i];
            k -= x;
        } else {
            LL d = k / (LL) (i + 1);
            for (LL j = 0; j <= i; j++)
                a[j] += d;
            k -= (i + 1) * d;
            for (LL j = i; j >= 0; j--)
                if (k) {
                    ++a[j];
                    --k;
                }
            break;
        }
    }
    LL sum = 0;
    for (LL i = n - 1; i >= 0; i--)
        sum += dlt[i], a[i] += sum;
    LL d = k / (LL) n;
    k -= n * d;
    for (LL i = n - 1; i >= 0; i--)
        a[i] += d;
    for (LL i = n - 1; i >= 0; i--)
        if (k) {
            ++a[i];
            --k;
        }
    LL res = 0;
    for (LL i = n - 1; i >= 0; i--)
        if (a[i] > a[0])
            ++res;
    cout << a[0] * n - n + 1 + res << endl;
}

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

2D1(1B1).Reverse Card (Easy Version)(数学)

题意:

给你两个正整数nnn,mmm

请计算满足以下条件的有序数对(a,b)(a,b)(a,b)的个数:

  • 1≤a≤n1\le a\le n1an,1≤b≤m1\le b\le m1bm;
  • a+ba+ba+bb⋅gcd⁡(a,b)b\cdot\gcd(a,b)bgcd(a,b)的倍数。

分析:

gcd(a,b)gcd(a,b)gcd(a,b)一定是等于bbb,问题转换为(a+b)%(b∗b)==0(a+b)\%(b*b)==0(a+b)%(bb)==0

那么aaabbb的倍数一定成立,可以通过枚举bbb来找aaa

代码:

#include<bits/stdc++.h>

using namespace std;

void solve() {
    int n, m;
    cin >> n >> m;
    int ans = 0;
    for (int b = 1; b <= m; ++b) {
        ans += (n / b + 1) / b - (b == 1);
    }
    cout << ans << endl;
}

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

2D2(1B2).Reverse Card (Hard Version)(数学)

题意:

给你两个正整数nnn,mmm

请计算满足以下条件的有序数对(a,b)(a,b)(a,b)的个数:

  • 1≤a≤n1\le a\le n1an,1≤b≤m1\le b\le m1bm;
  • b⋅gcd⁡(a,b)b\cdot\gcd(a,b)bgcd(a,b)a+ba+ba+b的倍数。

分析:

gcd⁡(a,b)\gcd(a,b)gcd(a,b)表示为ddd。假设有a=pda=pda=pdb=qdb=qdb=qd,那么我们知道gcd⁡(p,q)=1\gcd(p,q)=1gcd(p,q)=1.

(a+b)∣(b⋅gcd⁡(a,b))  ⟺  (pd+qd)∣(qd2)  ⟺  (p+q)∣(qd)(a+b)\mid(b\cdot\gcd(a,b))\iff(pd+qd)\mid(qd^2)\iff(p+q)\mid (qd)(a+b)(bgcd(a,b))(pd+qd)(qd2)(p+q)(qd)

已知gcd⁡(p+q,q)=gcd⁡(p,q)=1\gcd(p+q,q)=\gcd(p,q)=1gcd(p+q,q)=gcd(p,q)=1,所以是(p+q)∣d(p+q)\mid d(p+q)d

我们还知道p≥1,q≥1p\ge 1,q\ge 1p1,q1,所以p<d=ap≤npp\lt d=\frac{a}{p}\le\frac{n}{p}p<d=papn,从而 p2<np^2\lt np2<n。同样,我们可以证明q2<mq^2\lt mq2<m

所以(p,q)(p,q)(p,q)的个数是O(nm)=O(n+m)\mathcal O(\sqrt{nm})=\mathcal O(n+m)O(nm)=O(n+m)。我们可以枚举出gcd⁡(p,q)=1\gcd(p,q)=1gcd(p,q)=1的每个(p,q)(p,q)(p,q),并计算出答案。需要(p+q)∣d(p+q)\mid d(p+q)d,因此我们加上⌊min⁡{⌊np⌋,⌊mq⌋}p+q⌋\left\lfloor\frac{\min\{\lfloor\frac{n}{p}\rfloor,\lfloor\frac{m}{q}\rfloor\}}{p+q}\right\rfloorp+qmin{⌊pn,qm⌋}

时间复杂度:O(∑n+∑m)\mathcal O(\sum n+\sum m)O(n+m)

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int main() {
    int t;
    cin >> t;
    while (t--) {
        ll n, m;
        cin >> n >> m;
        ll sq = sqrt(n) + 2, sqm = sqrt(m) + 2;
        vector bad(sq + 1, vector<bool>(sqm + 1, 0));
        for (ll i = 2; i <= min(sq, sqm); i++) {
            for (ll a = i; a <= sq; a += i) {
                for (ll b = i; b <= sqm; b += i) {
                    bad[a][b] = true;
                }
            }
        }
        ll ans = 0;
        for (ll a = 1; a * a <= n; a++) {
            for (ll b = 1; b * b <= m; b++) {
                if (bad[a][b]) continue;
                ans += min(n / (a + b) / a, m / (a + b) / b);
            }
        }
        cout << ans << endl;
    }
    return 0;
}

赛后交流

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

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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值