其他分享
首页 > 其他分享> > CF Round 789 Div2 题解

CF Round 789 Div2 题解

作者:互联网

比赛链接

A题 Tokitsukaze and All Zero Sequence(思维)

给定一个长度为 \(n\) 的数列 \(\{a_n\}\)。

我们可以进行若干次操作,每次操作都选定两个位置 \(i.j\),随后:

  1. 若 \(a_i=a_j\),可以选择将 \(a_i\) 或 \(a_j\) 变为 0
  2. 若 \(a_i\not=a_j\),可以选择将 \(a_i,a_j\) 都变为 \(\min(a_i,a_j)\)

问至少需要多少次操作,可以使得数列变为全空?

共 \(T(T\leq 10^3)\) 组数据,每组数据中 \(2\leq n\leq 100,0\leq a_i\leq 100\)

每次操作至多将一个非 0 的数变为 0,所以我们在数列中有 0 的情况下,输出 \(n\) 减去 0 的数量即可。

当所有数都不是 0 的时候,那么必须先把一个数变成 0,随后执行上面的操作:

  1. 存在两个相同的数字,先将其中一个变成 0,然后依次把其他的数变为 0即可,总次数 \(n\)
  2. 所有数各不相同,那么先执行一个操作2,等到他俩相同后就执行上面的流程,总次数 \(n+1\)

判断每个数是否相同,可以开一个桶,也可以直接 set 或者排序处理。

#include <bits/stdc++.h>
using namespace std;
const int N = 210;
int n, a[N], v[N];
int solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    memset(v, 0, sizeof(v));
    for (int i = 1; i <= n; ++i)
        ++v[a[i]];
    if (v[0] > 0) return n - v[0];
    else {
        for (int i = 0; i <= 100; ++i)
            if (v[i] > 1) return n;
        return n + 1;
    }
}

int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

B1题 Tokitsukaze and Good 01-String (easy version)(贪心)

给定一个长度为 \(n\)(必然是偶数),仅包含 01 的字符串。

现在可以执行操作,每次可以选择将某一位进行变换(01互换)。

问,至少要执行多少次操作,才能使得数列符合性质?(性质直接看原题面)

\(2\leq n \leq 2*10^5\)

我们先把他们划分下来,随后奇数段记为 1,偶数段记为 0。

从前扫到后,每当该段为 1 的时候,就要花费 1 的代价使得这一段变为 0,下一段互换,最后输出代价和即可,复杂度 \(O(n)\)。

#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n;
char s[N];
int vec[N];
int solve() {
    //read & init
    scanf("%d%s", &n, s + 1);
    int tot = 0, cnt = 1;
    for (int i = 2; i <= n; ++i)
        if (s[i] == s[i - 1]) ++cnt;
        else vec[++tot] = cnt, cnt = 1;
    vec[++tot] = cnt;
    //solve
    int res = 0;
    for (int i = 1; i <= tot; ++i)
        if (vec[i] % 2) vec[i]--, vec[i + 1]++, ++res;
    return res;
}

int main()
{
    int T;
    cin >> T;
    while (T--) printf("%d\n", solve());
    return 0;
}

C题 Tokitsukaze and Strange Inequality(前缀和,枚举优化)

给定一个长度为 \(n\) 的排列 \(\{p_n\}\),问存在多少四元组 \((a,b,c,d)\),满足 \(1\leq a<b<c<d\leq n,p_a<p_c,p_b>p_d\) ?

\(1\leq n \leq 5000\)

我们考虑枚举 \(b,c\),那么就变成了维护 \([1,b-1],[c+1,n]\) 这两个区间:每次 \(O(1)\) 查询 \([1,b-1]\) 上有多少数小于 \(p_c\),\([c+1,n]\) 上有多少数小于 \(p_b\)。

考虑到至多只有 \(n\) 种不同的数(题目给定了排列,都不用离散化),所以我们开一个数组 \(f_{i,j}\),表示前 \(i\) 个数中小于 \(j\) 的元素个数。同样的,\(g_{i,j}\) 表示后 \(i\) 个数中小于 \(j\) 的元素个数。

随后对于每个枚举的 \(b,c\),根据乘法原理,给答案加上 \(f_{b-1,p_c}*g_{c+1,p_b}\) 即可。

时空复杂度均为 \(O(n^2)\),有点小紧。

本题有一个类似的姊妹题:CF1400D Zigzags

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 5010;
int n, p[N];
int f[N][N], g[N][N];
LL solve() {
    cin >> n;
    for (int i = 1; i <= n; ++i)
        cin >> p[i];
    for (int i = 1; i <= n; ++i)
        f[0][i] = g[n + 1][i] = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            f[i][j] = f[i - 1][j] + (p[i] < j);
    for (int i = n; i >= 1; i--)
        for (int j = 1; j <= n; ++j)
            g[i][j] = g[i + 1][j] + (p[i] < j);
    LL res = 0;
    for (int b = 1; b < n; ++b)
        for (int c = b + 1; c < n; ++c)
            res += f[b - 1][p[c]] * g[c + 1][p[b]];
    return res;
}

int main()
{
    int T;
    cin >> T;
    while (T--) cout << solve() << endl;
    return 0;
}

标签:return,int,题解,789,CF,leq,solve,操作,--
来源: https://www.cnblogs.com/cyhforlight/p/16255006.html