Codeforces Round 970 (Div. 3) A~F

A.Sakurako’s Exam (思维)

题意:

S a k u r a k o Sakurako Sakurako有一场数学考试。老师给了一个数组,由 a a a个 1 和 b b b个 2 组成。

在数组中, S a k u r a k o Sakurako Sakurako必须在每个元素前面放置一个 +-,以使数组中所有元素的总和等于 0 0 0

S a k u r a k o Sakurako Sakurako不确定是否有可能解决这个问题,因此确定是否有办法分配符号,使得数组中所有元素的总和等于 0 0 0

分析:

a , b a,b a,b都为偶数时候,一定可以使得和为 0 0 0。只需要对半符号相反即可。当 a a a为偶数, b b b可以是奇数也可以是偶数。例如1 1 2符合条件,1 1 2 2也符合条件。当 a a a是奇数个时,不满足题意。当 a , b a,b a,b其中一个为 0 0 0时,另外一个要为偶数个。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        int a, b;
        cin >> a >> b;
        if (a == 0)
        {
            if (b % 2)
                cout << "NO" << endl;
            else
                cout << "YES" << endl;
        }
        else if (b == 0)
        {
            if (a % 2)
                cout << "NO" << endl;
            else
                cout << "YES" << endl;
        }
        else if (a % 2)
            cout << "NO" << endl;
        else
            cout << "YES" << endl;
    }
    return 0;
}

B.Square or Not (思维)

题意:

精美的二进制矩阵是边缘为 1 1 1而内部为 0 0 0的矩阵。 S a k u r a k o Sakurako Sakurako有一个大小为 r × c r \times c r×c的精美二进制矩阵,并通过写下矩阵的所有行(从第一行开始到第 r r r行结束)创建了一个二进制字符串 s s s。换句话说,矩阵中第 i i i行和 j j j列的元素对应于字符串的第 ( ( i − 1 ) ∗ c + j ) ((i-1)*c+j) ((i1)c+j)个元素。 现在你需要检查字符串 s s s所构造的矩阵是不是正方形。

分析:

首先考虑读入的字符串长度是否能构造成正方形,其次因为矩阵的外围为 1 1 1,中间为 0 0 0,遍历到 时 ( i , j ) (i,j) (i,j),看字符串对应位置 i × n + j i \times \sqrt{n} + j i×n +j的字符是否为对应字符即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
void solve()
{
    int n;
    string s;
    cin >> n >> s;
    int m = sqrt(n);
    if (m * m != n)
    {
        cout << "No" << endl;
        return;
    }
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < m; j++)
        {
            char c = '0';
            if (!i || !j || i == m - 1 || j == m - 1)
                c = '1';
            if (s[i * m + j] != c)
            {
                cout << "No" << endl;
                return;
            }
        }
    }
    cout << "Yes" << endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

C. Longest Good Array (贪心)

题意:

S a k u r a k o Sakurako Sakurako正在研究数组。长度为 n n n的数组 a a a被认为是好的,当且仅当:

  • 数组 a a a是递增的,并且对于所有 2 ≤ i ≤ n 2 \le i \le n 2in都是 a _ i − 1 < a _ i a\_{i - 1} < a\_i a_i1<a_i

  • 相邻元素之间的差异是递增的,意味着对于所有 2 ≤ i < n 2 \le i < n 2i<n都是 a _ i − a _ i − 1 < a _ i + 1 − a _ i a\_i - a\_{i-1} < a\_{i+1} - a\_i a_ia_i1<a_i+1a_i

S a k u r a k o Sakurako Sakurako已经确定了 l l l r r r,并希望构造一个长度最大的好数组,其中对于所有 a _ i a\_i a_i都是 l ≤ a _ i ≤ r l \le a\_i \le r la_ir

帮助 S a k u r a k o Sakurako Sakurako找到给定 l l l r r r的好数组的最大长度。

分析:

我们贪心的考虑这个问题,让差值每次递增 1 1 1,例如 l , l + 1 , l + 3 , l + 6 l,l+1,l+3,l+6 l,l+1,l+3,l+6这样进行构造,再二分答案能构造的最大长度即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
void solve()
{
    LL L, R;
    cin >> L >> R;
    LL l = 0, r = R;
    while (l < r)
    {
        LL mid = l + r + 1 >> 1;
        LL sum = (1 + mid) * mid / 2;
        if (L + sum <= R)
            l = mid;
        else
            r = mid - 1;
    }
    cout << r + 1 << endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

D. Sakurako’s Hobby (枚举)

题意:

对于某个排列 p p p S a k u r a k o Sakurako Sakurako规定整数 j j j可以通过 i i i到达,当且仅当可以通过进行若干次 i = p _ i i=p\_i i=p_i的操作 ,使 i i i等于 j j j

如果是 p = [ 3 , 5 , 6 , 1 , 2 , 4 ] p=[3,5,6,1,2,4] p=[3,5,6,1,2,4],那么例如, 4 4 4可以从 1 1 1到达,因为: i = 1 i=1 i=1 → \rightarrow i = p _ 1 = 3 i=p\_1=3 i=p_1=3 → \rightarrow i = p _ 3 = 6 i=p\_3=6 i=p_3=6 → \rightarrow i = p _ 6 = 4 i=p\_6=4 i=p_6=4。现在为 i = 4 i=4 i=4,因此 4 4 4可从 1 1 1到达。

排列中的每个数字都被涂成黑色或白色。

S a k u r a k o Sakurako Sakurako将函数 F ( i ) F(i) F(i)定义为可从 i i i到达的黑色整数的数量。

请计算出每个 1 ≤ i ≤ n 1\le i\le n 1in F ( i ) F(i) F(i)的值。

分析:

考虑到题目给出的是排列,那么所有元素在按照规则遍历时,会出现环状结构(最后到达本身),这个环上的所有元素 F ( i ) F(i) F(i)都相等。我们逐个循环枚举就可以求出所有的 F ( i ) F(i) F(i)

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
void solve()
{
    LL n;
    cin >> n;
    LL p[n + 1] = {0}, b[n + 1] = {0};
    int us[n + 1] = {0};
    for (int i = 1; i <= n; i++)
    {
        cin >> p[i];
    }
    string s;
    cin >> s;
    for (int i = 1; i <= n; i++)
    {
        if (us[i])
            continue;
        int sz = 0;
        while (!us[i])
        {
            us[i] = 1;
            sz += s[i - 1] == '0';
            i = p[i];
        }
        while (us[i] != 2)
        {
            b[i] = sz;
            us[i] = 2;
            i = p[i];
        }
    }
    for (int i = 1; i <= n; i++)
    {
        cout << b[i] << " ";
    }
    cout << endl;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

E. Alternating String (dp)

题意:

S a k u r a k o Sakurako Sakurako非常喜欢交替字符串。如果偶数位置上的字符相同,奇数位置上的字符相同,并且字符串的长度为偶数,她将小写拉丁字母的字符串 s s s称为交替字符串。

例如,字符串 ababgg 是交替的,而字符串 abaggwp 则不是。

现在,你可以对字符串执行两种类型的操作:

  1. 选择一个索引 i i i,并从字符串中删除第 i i i个字符,这将使字符串的长度减少 1 1 1。这种类型的操作最多只能执行 1 1 1次;

  2. 选择索引 i i i,并将 s _ i s\_i s_i替换为任何其他字母。

给出使字符串变为交替字符串所需的最少操作数。

分析:

因为只能删除一次字符,所以长度为奇数时才考虑删除。我们令 n u m [ i ] [ j ] num[i][j] num[i][j]表示奇数位或者偶数位,各种字符出现的次数。当 n n n为偶数时,分别找到奇数位与偶数位出现最多字符的次数,这些字符保持不变。那么有 a n s = n − m a x ( n u m _ 0 , n u m _ 0 + 25 ) − m a x ( n u m _ 1 , n u m _ 1 + 25 ) ; ans=n-max(num\_0,num\_0+25)-max(num\_1,num\_1+25); ans=nmax(num_0,num_0+25)max(num_1,num_1+25);。当 n n n为奇数时,我们只要遍历 1 − n 1-n 1n,表示删除该字符后的操作次数,最终去最小值即可。 我们再令 t m p [ i ] [ j ] tmp[i][j] tmp[i][j]表示在当前位置上各个字符出现的次数,再令 c n t [ i ] [ j ] cnt[i][j] cnt[i][j]表示删除该位置上字符后整个字符串上奇偶字符数。那么有 c n t [ 0 ] [ j ] = t m p [ 0 ] [ j ] + n u m [ 1 ] [ j ] − t m p [ 1 ] [ j ] cnt[0][j]=tmp[0][j]+num[1][j]-tmp[1][j] cnt[0][j]=tmp[0][j]+num[1][j]tmp[1][j] c n t [ 1 ] [ j ] = t m p [ 1 ] [ j ] + n u m [ 0 ] [ j ] − t m p [ 0 ] [ j ] cnt[1][j]=tmp[1][j]+num[0][j]-tmp[0][j] cnt[1][j]=tmp[1][j]+num[0][j]tmp[0][j]

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 998244353;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n;
        string s;
        cin >> s;
        int ans = n;
        int cnt[2][26]{};
        for (int i = 0; i < n; i++)
        {
            cnt[i % 2][s[i] - 'a']++;
        }
        if (n % 2 == 0)
        {
            ans = ans - *max_element(cnt[0], cnt[0] + 26) - *max_element(cnt[1], cnt[1] + 26);
        }
        for (int i = n - 1; i >= 0; i--)
        {
            cnt[i % 2][s[i] - 'a']--;
            if (n % 2)
            {
                ans = min(ans, n - *max_element(cnt[0], cnt[0] + 26) - *max_element(cnt[1], cnt[1] + 26));
            }
            cnt[(i + 1) % 2][s[i] - 'a']++;
        }
        cout << ans << endl;
    }
    return 0;
}

F. Sakurako’s Box (数学)

题意:

S a k u r a k o Sakurako Sakurako有一个装有 n n n个球的盒子。每个球都有其价值。她想和朋友打赌,朋友能否从盒子中随机挑选两个球(可能是两个不同的球,但它们的价值可能相同),它们的价值乘积将与 S a k u r a k o Sakurako Sakurako猜测的数字相同。 现在你要帮他找出数组中两个元素乘积的期望值。 可以证明期望值的形式为 P Q \frac{P}{Q} QP,其中 P P P Q Q Q为非负整数,并且为 Q ≠ 0 Q \ne 0 Q=0。输出 P ⋅ Q − 1 (   m o d   1 0 9 + 7 ) P \cdot Q^{-1}(\bmod 10^9+7) PQ1(mod109+7)的值。

分析:

数组中数对的数量为 C _ n 2 = n ∗ ( n − 1 ) 2 C\_n^2 = \frac{n*(n-1)}{2} C_n2=2n(n1),所有数对的乘积和为 s u m = ∑ _ i = 2 n ∑ _ j = 1 i − 1 a _ j ∗ a _ i sum = \sum\_{i=2}^n \sum\_{j=1}^{i-1}a\_j*a\_i sum=_i=2n_j=1i1a_ja_i,那么期望值就是 s u m C 2 _ n \frac{sum}{C^2\_{n}} C2_nsum。我们再用前缀和进行优化,令 f ( i ) = ∑ _ j = 1 i a _ i f(i) = \sum\_{j=1}^i a\_i f(i)=_j=1ia_i s u m = ∑ _ i = 2 n a _ i ∗ ∑ _ j = 1 i − 1 a _ j = ∑ _ i = 2 n a _ i ∗ f ( i − 1 ) sum=\sum\_{i=2}^n a\_i *\sum\_{j=1}^{i-1}a\_j = \sum\_{i=2}^n a\_i * f(i-1) sum=_i=2na_i_j=1i1a_j=_i=2na_if(i1)。再用快速幂算出逆元进行计算即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define endl '\n'
#define PII pair<LL, LL>
const int N = 3e5 + 10;
const int InF = 2e9 + 5;
const int mod = 1e9 + 7;
LL qmi(LL a, int b)
{
    LL res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        LL n;
        cin >> n;
        LL pre = 0, sum = 0;
        for (int i = 1; i <= n; i++)
        {
            LL x;
            cin >> x;
            if (i > 1)
            {
                (sum += pre * x % mod) %= mod;
            }
            (pre += x) %= mod;
        }
        LL ans = sum * qmi(n * (n - 1) % mod * qmi(2, mod - 2) % mod, mod - 2) % mod;
        cout << ans << endl;
    }
    return 0;
}

赛后交流

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值