Practice.

A. Password

题意

给定 \([0, 9]\) 中的几个元素,按照升序排列给出。排除这些元素后,剩余的元素可作为密码的一部分。

其中,密码为 \(4\) 位数,只包含两种数字,且每种数字出现两次。

输出密码可行解的个数。

思路

首先,我们假设只包含数字 \(a, b\),那么有 \(aabb, abba, bbaa, abab, baba\)\(6\) 种可行解。

其次,我们统计出剩余 \(k\) 种数字,那么选两个的方案数就是 \(C^2_k\)

因此,答案就是 \(6C^2_k\)

不过,既然元素个数已经告诉我们了,那么 \(k = 10 - n\)

因此我们完全不用管元素是什么。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 2e5 + 10, inf = 0x3f3f3f3f3f3f3f3f, mod = 998244353;

void solve(){
    int n;
    cin >> n;
    for(int i=0;i<n;i++){
        int tmp;
        cin >> tmp; //啥用没有
    }
    cout << (10 - n) * (9 - n) / 2 * 6 << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin >> t;
    while(t --) solve();
}

Happy Happy Happy

B. Permutation Value

题意

给定一个整数 \(n\),构造一个排列,满足其所有连续子序列中排列的个数最少。

思路1

很直接的思路就是让连续的数用其他元素隔开。

并且,为了尽可能不构造出排列,我们就最好先隔一位填按顺序上答案,然后在剩余的位置按顺序填上剩余的数。

\(4\ 1\ 5\ 2\ 6\ 3\)

时间复杂度:\(O(n)\)

思路2

我们可能会想到倒着输出,这样确实可以让前面的那些取法都不是排列,但是我们只要选择右边界为右端点,那么我们会多出 \(n - 1\) 个排列,因为有 \(1\)

那么,很直接的思路就是把 \(1\) 直接移到第一个,然后倒序输出即可,不难发现和思路 \(1\) 得到的数量是一致的。

\(1\ 6\ 5\ 4\ 3\ 2\)

时间复杂度:\(O(n)\)

对应AC代码(思路1)

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 2e5 + 10, inf = 0x3f3f3f3f3f3f3f3f, mod = 998244353;

void solve(){
    int n;
    cin >> n;
    if(n % 2 == 1) cout << 1 << ' ';
    for(int i=0;i<n/2;i++){
        cout << n / 2 + i + 1 + n % 2 << ' ' << i + 1 + n % 2 << ' ';
    }
    cout << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin >> t;
    while(t --) solve();
}

思路2是题解做法(没想到居然这么简单

C. Save the Magazines

题意

给定一个长为 \(n\) 的二进制字符串 \(s\) 以及一个包含 \(n\) 个元素的序列 \(a\)。定义对于所有 \(i \in [2, n]\),可进行下面的两个操作任选一:

  1. \(s_{i - 1} = 0, s_i = 1\),交换 \(a_{i - 1}\)\(a_i\)(即传递 \(1\) 到前者);

  2. 不进行操作

规定同一个 \(1\) 只能被传递一次。

遍历操作后的 \(s\),若 \(s_i = 1\),累计 \(ans += a_i\)

输出 \(ans\) 的最大值。

思路

我们不妨遍历所有连续的 \(1\),然后找出其中的最小值,将这个最小值剔除,用前面的 \(0\) 代替。

或者更容易实现地,我们遍历找出连续的 \(1\) 前面相邻的那个 \(0\),并从这个 \(0\) 开始寻找最小值,并统计总和,最后这一段的价值就是 \(sum - min\)

为了更加方便,我们不妨在第一个元素前插入 \(0\),那么 \(0\) 一定是最小的,如果开头是 \(1\) 也不会影响答案。

时间复杂度:\(O(n)\)

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>

const int N = 2e5 + 10, inf = 0x3f3f3f3f3f3f3f3f, mod = 998244353;

int a[N];

void solve(){
    int n;
    cin >> n;
    string s;
    cin >> s;
    s = '0' + s;
    int ans = 0;
    for(int i=1;i<=n;i++) cin >> a[i];
    for(int i=0;i<=n;){
        int mn = a[i];
        int sum = a[i];
        int j = i + 1;
        while(j <= n && s[j] == '1'){
            mn = min(mn, a[j]);
            sum += a[j];
            j ++;
        }
        i = j;
        ans += sum - mn;
    }
    cout << ans << '\n';
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin >> t;
    while(t --) solve();
}

有个大蠢比没看到只能向前交换然后一直在想 \(dp\),我不说是谁(x