Codeforces think cell Round 1 A~E

A.Maximise The Score(贪心)

题意:

白板上写着 2 n 2n 2n 个正整数。你觉得无聊,决定用白板上的数字玩一个单人游戏。

开始时,你的分数为 0 0 0。你可以通过走下面的棋 n n n次来增加你的分数:

  • 选择写在白板上的两个整数 x x x y y y
  • min ⁡ ( x , y ) \min(x,y) min(x,y)加到您的分数中。
  • 擦去白板上的 x x x y y y

注意,在走完 n n n步之后,白板上不会有任何整数。

如果你以最佳方式走完 n n n步棋,计算你最终能得到的最高分。

分析:

将数组从大到小排序,每次加分为两数中的最小值,能够发现数组中的最大值一定不会作为分数加入,若一次选择最大值与任意数,则可以将任意数加入分数,所以每次选择第二大的值与最大值一起被删除,重复此操作即为最优。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 2e5 + 10;

int a[N];

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 0; i < 2 * n; i++) cin >> a[i];
        sort(a, a + 2 * n);
        int ans = 0;
        for (int i = 0; i < n; i++) ans += a[2 * i];
        cout << ans << endl;
    }
    return 0;
}

B.Permutation Printing(模拟)

题意:

给你一个正整数 n n n

请找出一个长度为 n n n的排列 † ^\dagger 。要求长度为 n n n的排列 p p p中,不存在两个不同的索引 i i i j j j 1 ≤ i , j < n 1\leq i,j \lt n 1i,j<n i ≠ j i\neq j i=j),使得 p i p_i pi整除 p j p_j pj p i + 1 p_{i+1} pi+1整除 p j + 1 p_{j+1} pj+1。( 1 ≤ i , j < n 1\leq i,j \lt n 1i,j<n; i ≠ j i\neq j i=j)。

在此问题的限制条件下,可以证明至少存在一个 p p p

† ^\dagger 长度为 n n n的排列是由 n n n个不同的整数组成的数组,这些整数从 1 1 1 n n n依次排列。例如, [ 2 , 3 , 1 , 5 , 4 ] [2,3,1,5,4] [2,3,1,5,4]是一个排列,但 [ 1 , 2 , 2 ] [1,2,2] [1,2,2]不是一个排列( 2 2 2出现了两次), [ 1 , 3 , 4 ] [1,3,4] [1,3,4] 也不是一个排列( n = 3 n=3 n=3,但数组中有 4 4 4)。

分析:

任意 > n 2 \gt \frac{n}{2} >2n的数字都不可能是任何数字的倍数。若我们大小交替排序,如果 a i a_i ai能够整除 a j a_j aj,那么一定会有 a i + 1 > a j + 1 a_{i+1}\gt a_{j+1} ai+1>aj+1,不可能满足整除,交替排序即可。

代码:

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        int i = 1, j = n;
        while (i < j) {
            cout << i << ' ' << j << ' ';
            i++;
            j--;
        }
        if (i == j)
            cout << i << ' ';
        cout << endl;
    }
    return 0;
}

C.Lexicographically Largest(模拟)

题意:

史塔克有一个长度为 n n n的数组 a a a。他还有一个空集 S S S。注意, S S S并不是一个多重集。

他将进行以下三步操作恰好 n n n次:

  1. 选择一个序号 i i i,使得 1 ≤ i ≤ ∣ a ∣ 1\leq i\leq|a| 1ia
  2. † ^\dagger a i + i a_i+i ai+i插入 S S S
  3. a a a中删除 a i a_i ai。注意, a i a_i ai右边所有元素的索引将减少 1 1 1

注意, n n n次操作后, a a a将为空。

现在,史塔克将构造一个新数组 b b b,这个数组以 S S S降序排列。形式上, b b b是一个大小为 ∣ S ∣ |S| S的数组,其中 b i b_i bi S S S的第 i i i大元素( 1 ≤ i ≤ ∣ S ∣ 1\leq i\leq|S| 1iS)。

求史塔克操作后可以产生的字典序最大 ‡ ^\ddagger 的数组 b b b

† ^\dagger 集合只能包含唯一的元素。插入一个已经存在于集合中的元素不会改变集合的元素。

‡ ^\ddagger 如果且仅当以下情况之一成立时,数组 p p p在字典序上大于序列 q q q

  • q q q p p p的前缀,但 p ≠ q p≠q p=q
  • 或者在数组 p p p q q q不同的第一个位置上,数组 p p p中有一个比 q q q中对应元素更大的元素。

注意, [ 3 , 1 , 4 , 1 , 5 ] [3,1,4,1,5] [3,1,4,1,5] 按字典序大于 [ 3 , 1 , 3 ] [3,1,3] [3,1,3] [   ] [\,] [] [ 3 , 1 , 4 , 1 ] [3,1,4,1] [3,1,4,1],但不大于 [ 3 , 1 , 4 , 1 , 5 , 9 ] [3,1,4,1,5,9] [3,1,4,1,5,9] [ 3 , 1 , 4 , 1 , 5 ] [3,1,4,1,5] [3,1,4,1,5] [ 4 ] [4] [4]

分析:

题意为先确定所有数后再进行降序去重排序,而不是先移除的数一定在后移除的数之前。因为最后会进行排序,集合中的数越大越好,先选择前面的数后面索引 i i i会减小,所以先选后面的数字插入集合是最优的。

考虑集合要去重,如果数组 q q q是数组 p p p的前缀且 p ≠ q p≠q p=q那么 p p p的字典序大于 q q q。所以需要将相同的数字减少去重达到增大数组长度的目的。即对于相同的数字,优先取其前面的数,使其索引减少,如此最终集合的长度一定可以取到 n n n

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL N = 3e5 + 10;
int a[N];

bool cmp(int x, int y) {
    return x > y;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            a[i] += i;
        }
        sort(a + 1, a + 1 + n, cmp);
        for (int i = 2; i <= n; i++) {
            a[i] = min(a[i], a[i - 1] - 1);
        }
        for (int i = 1; i <= n; i++) {
            cout << a[i] << " ";
        }
        cout << endl;
    }
    return 0;
} 

D.Sum over all Substrings(动态规划)

题意:

题目两个版本的唯一区别在于对 t t t n n n的限制。

对于长度均为 m m m的二进制 † ^\dagger 模式 p p p和二进制字符串 q q q,如果对于每个 i i i( 1 ≤ i ≤ m 1\leq i\leq m 1im),都存在索引 l l l r r r满足下列要求,那么 q q q称为 p − g o o d p-good pgood

  • 1 ≤ l ≤ i ≤ r ≤ m 1\leq l\leq i\leq r\leq m 1lirm
  • p i p_i pi是字符串 q l , q l + 1 , … , q r q_l,q_{l+1},\ldots,q_{r} ql,ql+1,,qr的模式 ‡ ^\ddagger

对于模式 p p p来说, f ( p ) f(p) f(p)是二进制字符串 p p p(长度与模式相同)中 1 \mathtt{1} 1个数的最小值。

给你一个大小为 n n n的二进制字符串 s s s。求
∑ i = 1 n ∑ j = i n f ( s i s i + 1 … s j ) \sum_{i=1}^{n} \sum_{j=i}^{n} f(s_i s_{i+1} \ldots s_j) i=1nj=inf(sisi+1sj)

即对 s s s的所有 n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1)子串中 f f f的值求和。

† ^\dagger 二进制模式是指仅由字符 0 \mathtt{0} 0 1 \mathtt{1} 1组成的字符串。

‡ ^\ddagger 如果 t t t c c c的出现次数至少为 ⌈ m 2 ⌉ \lceil\frac{m}{2}\rceil 2m,则字符 c c c是长度为 m m m的字符串 t t t的模式。例如, 0 \mathtt{0} 0 010 \mathtt{010} 010的模式, 1 \mathtt{1} 1 不是 010 \mathtt{010} 010的模式,而 0 \mathtt{0} 0 1 \mathtt{1} 1都是 011010 \mathtt{011010} 011010的模式。

分析:

题意较难理解。 D 1 D1 D1的数据量可以通过暴力计算每一个子区间的答案解决。枚举所有 [ l , r ] [l,r] [l,r]。设 d p i dp_i dpi代表 1 − i 1-i 1i所有前缀的答案。发现 010 010 010的结构可以把长度 3 3 3的覆盖。长度 2 2 2和长度 1 1 1差不多。因此 d p i dp_i dpi可以转移到 d p i − 1 dp_{i-1} dpi1 d p i − 2 dp_{i-2} dpi2 d p i − 3 dp_{i-3} dpi3

D 2 D2 D2枚举 r r r。发现除 [ r , r ] [r,r] [r,r] [ r − 1 , r ] [r-1,r] [r1,r]以外,其他的一定是 [ r − 2 , r ] [r-2,r] [r2,r]要在 d p dp dp中是最佳转移。因此可以进行优化。

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL mod = 998244353;

void solve() {
    int n;
    cin >> n;
    string s;
    cin >> s;
    s = " " + s;
    LL ans = 0;
    vector<LL> dp(n + 1, 0);//坑点,使用数组初始化会超时
    for (int i = 1; i <= n; i++) {
        LL t = ans;
        if (s[i] == '1') {
            ans += s[i] - '0';
            if (i >= 2)
                ans += (s[i] - '0' + s[i - 1] - '0' + 1) / 2;
            if (i >= 3)
                ans += dp[i - 3] + (i - 2) * ((s[i] - '0' + s[i - 1] - '0' + s[i - 2] - '0' + 2) / 3);
        } else {
            ans += dp[i - 1];
        }
        dp[i] = ans - t;
    }
    cout << ans << endl;
}

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

E.2…3…4… Wonderful! Wonderful!(数学)

题意:

史塔克有一个长度为 n n n的数组 a a a,其中对于所有 i i i( 1 ≤ i ≤ n 1\leq i\leq n 1in), a i = i a_i=i ai=i。他将选择一个正整数 k k k( 1 ≤ k ≤ ⌊ n − 1 2 ⌋ 1\leq k\leq\lfloor\frac{n-1}{2}\rfloor 1k2n1) 并对 a a a进行任意次数(可能是 0 0 0次)的以下运算。

  • † ^\dagger a a a中选择长度为 2 ⋅ k + 1 2\cdot k+1 2k+1的子序列 s s s。他会从 a a a中删除 s s s的前 k k k个元素。为了保持完全平衡,他还将从 a a a中删除 s s s的最后 k k k个元素。

史塔克想知道对于每个 k k k 1 ≤ k ≤ ⌊ n − 1 2 ⌋ 1\leq k\leq\lfloor\frac{n-1}{2}\rfloor 1k2n1),最后能有多少个数组 a a a。由于史塔克不擅长计算问题,他需要你的帮助。

由于数组的数目可能太大,答案对 998244353 998244353 998244353取模。

† ^\dagger 如果 x x x可以通过删除几个(可能是零个或全部)元素从 y y y得到,那么序列 x x x就是序列 y y y的子序列。例如, [ 1 , 3 ] [1,3] [1,3] [ 1 , 2 , 3 ] [1,2,3] [1,2,3] [ 2 , 3 ] [2,3] [2,3] [ 1 , 2 , 3 ] [1,2,3] [1,2,3]的子序列。 [ 3 , 1 ] [3,1] [3,1] [ 2 , 1 , 3 ] [2,1,3] [2,1,3]不是 [ 1 , 2 , 3 ] [1,2,3] [1,2,3]的子序列。

分析:

需要找出可能的 b b b个数。可以求出长度为 n n n的二进制字符串 s s s的个数,如果 i i i出现在 b b b中,则 s i = 1 s_i=1 si=1,否则 s i = 0 s_i=0 si=0

对于给定的 n n n k k k,如果存在可以从 a a a得到 b b b,我们称 s s s字符串。

对于本题,与其计算好的字符串 s s s,不如计算不坏的字符串的数量。

为了方便起见,只考虑 0 \mathtt{0} 0的个数能被 2 k 2k 2k整除的字符串 s s s

当且仅当在 s s s中从左边开始的 k k k 0 0 0和从右边开始的 k k k 0 0 0之间不存在任何 1 1 1时, s s s的。

把左边的第 k k k 0 0 0和右边的第 k k k 0 0 0之间的所有 0 \mathtt{0} 0压缩成一个 0 0 0,并称这个新字符串为 t t t。需要注意的是, t t t中将包含 2 k − 1 2k-1 2k1 0 \mathtt{0} 0 0 \mathtt{0} 0's。我们还可以观察到,对于每一个 t t t,都存在一个唯一的 s s s。因为 n n n k k k已经被固定。因此,恰好有 x x x 1 \mathtt{1} 1字符串 s s s的数量是 ( x + 2 k − 1 2 k − 1 ) {{x+2k-1}\choose{2k-1}} (2k1x+2k1) ,因为恰好有 ( x + 2 k − 1 2 k − 1 ) {{x+2k-1}\choose{2k-1}} (2k1x+2k1)个二进制字符串 t t t含有 2 k − 1 2k-1 2k1 0 \mathtt{0} 0以及 x x x 1 \mathtt{1} 1

最后,恰好 ( n x ) − ( x + 2 k − 1 2 k − 1 ) \binom{n}{x}-\binom{x+2k-1}{2k-1} (xn)(2k1x+2k1)好的二进制字符串 s s s具有 x x x 1 \mathtt{1} 1个和 n − x n-x nx 0 \mathtt{0} 0。不需要为从 1 1 1 n n n的每个 x x x找出这个值,因为 s s s 0 \mathtt{0} 0的个数( n − x n-x nx)应该是 2 k 2k 2k的倍数。 x x x中只有 O ( n 2 k ) O(\frac{n}{2k}) O(2kn)是有用的候选数。复杂度 O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n))

代码:

#include<bits/stdc++.h>

typedef long long LL;
using namespace std;
const LL mod = 998244353;
const LL N = 1e6 + 10;

LL fac[N], inv[N];

LL pw(LL x, LL y) {
    LL res = 1;
    while (y) {
        if (y & 1) {
            res = res * x % mod;
        }
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}

LL Inv(LL x) {
    return pw(x, mod - 2);
}

void init() {
    fac[0] = inv[0] = 1;
    for (int i = 1; i < N; i++) {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = Inv(fac[i]);
    }
}

LL C(LL x, LL y) {
    if (x < 0 || y < 0 || x < y)return 0;
    return fac[x] * inv[y] % mod * inv[x - y] % mod;
}

int main() {
    init();
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int k = 1; k * 2 < n; k++) {
            LL ans = 1;
            for (int t = 1; 2 * k * t < n; t++) {
                ans = (ans + C(n, 2 * k * t) - C(n - 2 * k * (t - 1) - 1, 2 * k - 1) + mod) % mod;
            }
            cout << ans << " ";
        }
        cout << endl;
    }
    return 0;
}

赛后交流

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

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

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值