Practice.

A. Compare T-Shirt Sizes

题意

比较 \(T\) 恤的尺码。

其中,\(XXL > XL, XXS < XS\)

思路

我们记 \(M = 0\),其余的按照 \(X\) 的个数计算即可。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

void solve() {
    string a, b;
    cin >> a >> b;
    int na = a.size(), nb = b.size();
    map<char, int> mp = {{'S', -1}, {'M', 0}, {'L', 1}};
    int va = mp[a[na - 1]] * na, vb = mp[b[nb - 1]] * nb;
    cout << (va == vb ? '=' : (va > vb ? '>' : '<')) << '\n';
}

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

无脑做即可

B. Funny Permutation

题意

给定整数 \(n\),构造一个长为 \(n\) 的排列 \(a\),满足 \(a_i\) 的至少一个相邻元素的值为 \(a_i ±1\),且 \(a_i \neq i\)

思路

首先,如果没有第二个条件,我们直接按顺序输出即可。

如果考虑第二个条件,如果 \(n\) 为偶数,那我们直接倒着输出即可。

\(n\) 为奇数的时候,我们就需要规避中间那个数不满足条件的情况了。

如何规避呢?我们只需先按顺序构造序列,然后把前 \(\frac{n + 1}{2}\) 个数移到最后就可以了。

可以证明,这种构造对任意长度的排列均满足。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

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

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

很有趣的构造题(x

C. Minimize the Thickness

题意

给定一个序列,将其分割为若干段 元素和相等 的片段,输出最长片段的长度最小值。

思路

很暴力。我们直接枚举第一段的长度,然后去分割,找出最长片段。最后我们统计最小值即可。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

void solve() {
    int n;
    cin >> n;
    vector<int> sum(n + 1);
    for(int i=1;i<=n;i++) {
        int cur;
        cin >> cur;
        sum[i] = sum[i - 1] + cur;
    }
    auto check = [n, sum](int cnt){
        bool ans = true;
        int pre = 0, mx = cnt;
        for(int i=1;i<=n;i++){
            if(sum[i] - sum[pre] > sum[cnt]){
                ans = false;
                break;
            }else if(sum[i] - sum[pre] == sum[cnt]){
                mx = max(mx, i - pre);
                pre = i;
            }
        }
        return make_pair(mx, ans && pre == n);
    };
    int ans = n;
    for(int i=1;i<=n;i++){
        auto e = check(i);
        if(e.sc) ans = min(ans, e.fs);
    }
    cout << ans << '\n';
}

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

无脑暴力.cpp

D. Masha and a Beautiful Tree

题意

给定一个满二叉树的所有叶节点的编号,规定编号序列为长度为 \(n\) 的排列。

判断是否可以通过 若干次 交换 任意 左右子树的位置 来让排列升序,若可以,输出最小操作数。

思路

显然,我们不难发现,两个父亲一样的叶节点的值相差 \(1\)

有趣的是,我们规定这些父亲节点(即第二层叶节点)的值为子节点的最大值除以 \(2\),那么依然需要满足上面的条件。

也就是说,我们只需从最底层向上合并并判断即可。

在每次判断的时候,我们比较一下左右两个节点的大小,如果左边的大,那么就需要一次操作,统计即可。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

void solve() {
    int n;
    cin >> n;
    vector<int> ori(n);
    for(int i=0;i<n;i++) cin >> ori[i];
    int p = 0, tp = n;
    while(tp > 0) tp /= 2, p ++;
    int ans = 0;
    for(int i=1;i<p;i++){
        int cnt = n / pow(2, i);
        vector<int> now(cnt);
        for(int j=0;j<cnt;j++){
            if(abs(ori[j * 2] - ori[j * 2 + 1]) != 1){
                ans = -1;
                break;
            }
            if(ori[j * 2] > ori[j * 2 + 1]) ans ++;
            now[j] = (ori[j * 2] + 1) / 2;
        }
        if(ans == -1) break;
        ori = now;
    }
    cout << ans << '\n';
}

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

找规律.jpg

E. Sending a Sequence Over the Network

题意

给定一种处理方式:将给定序列拆成若干段,在每段的前面或后面插入这一段的长度。

现在,给定一个序列,判断是否为处理后的序列。

思路

我们不妨考虑递推,即 \(dp\),用递推的方式记录当前位置之前的元素是否满足条件。

那么, 如果这个数是个数:

  1. 他是前面序列的个数,那么 \(dp[i]\ |= dp[i - a_i - 1]\) (该序列有效,那么 \(dp_i\) 和跳过这个序列的前一个序列的 \(dp\) 值有关)

  2. 他是后面序列的个数,那么 \(dp[i + a_i]\ |= dp[i - 1]\)

最后,\(dp[n]\) 即为答案。

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

对应AC代码

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int, int>
#define fs first
#define sc second

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

void init(){}

void solve() {
    int n;
    cin >> n;
    vector<int> dp(n + 1);
    dp[0] = true;
    for(int i=1;i<=n;i++){
        int cur;
        cin >> cur;
        if(i - cur >= 1) dp[i] |= dp[i - cur - 1];
        if(i + cur <= n) dp[i + cur] |= dp[i - 1];
    }
    cout << (dp[n] ? "YES\n" : "NO\n");
}

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

嘶,想到了还真不难