AtCoder Regular Contest 181 A~D

A.Sort Left and Right(思维)

题意:

给你一个 ( 1 , 2 , … , N ) (1,2,\dots,N) (1,2,,N) 的排列组合 P = ( P 1 , P 2 , … , P N ) P=(P_1,P_2,\dots,P_N) P=(P1,P2,,PN)

你想通过执行下面的操作零次或多次来满足所有 i = 1 , 2 , … , N i=1,2,\dots,N i=1,2,,N P i = i P_i=i Pi=i 要求:

  • 选择一个整数 k k k ,使得 1 ≤ k ≤ N 1 \leq k \leq N 1kN .如果是 k ≥ 2 k \geq 2 k2 ,则把 P P P 1 1 1 项到 ( k − 1 ) (k-1) (k1) 项按升序排序。然后,如果是 k ≤ N − 1 k \leq N-1 kN1 ,把 P P P ( k + 1 ) (k+1) (k+1) 项到 N N N 项按升序排序。

可以证明,在这个问题的约束条件下,对于所有的 i = 1 , 2 , … , N i=1,2,\dots,N i=1,2,,N ,对于任意的 P P P ,都可以用有限次的运算满足 P i = i P_i=i Pi=i 。请求解所需的最小运算次数。

分析:

分类讨论:

  • 若排列已经是升序:操作次数为 0 0 0
  • [ 1 , i ] [1,i] [1,i]已经是 1 − i 1-i 1i的排列,操作次数为 1 1 1
  • P 1 ≠ n P_1 \neq n P1=n或者 P n ≠ 1 P_n \neq 1 Pn=1操作次数为 2 2 2
  • 剩余情况操作次数为 3 3 3

代码:

#include <bits/stdc++.h>

using namespace std;

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    int f = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if (a[i] != i) {
            f = 1;
        }
    }
    if (!f) {
        cout << 0 << endl;
        return;
    }
    int mx = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i] == i) {
            if (mx == i - 1) {
                cout << 1 << endl;
                return;
            }
        }
        mx = max(a[i], mx);
    }
    if ((a[n] != 1) || (a[1] != n))
        cout << 2 << endl;
    else
        cout << 3 << endl;
}

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

B.Annoying String Problem(数学)

题意:

对于由小写英文字母组成的字符串 S S S T T T 以及由 01 组成的字符串 X X X ,请定义由小写英文字母组成的字符串 f ( S , T , X ) f(S,T,X) f(S,T,X) 如下:

  • 从空字符串开始,对于每个 i = 1 , 2 , … , ∣ X ∣ i=1,2,\dots,|X| i=1,2,,X ,如果 X X X 的第 i i i 个字符是 0,则将 S S S 追加到尾部,如果是 1,则将 T T T 追加到尾部。

给你一个由小写英文字母组成的字符串 S S S 以及由01组成的字符串 X X X Y Y Y

请判断是否存在一个字符串 T T T (可以为空),使得 f ( S , T , X ) = f ( S , T , Y ) f(S,T,X)=f(S,T,Y) f(S,T,X)=f(S,T,Y) .

分析:

首先,考虑 X X X Y Y Y1 的个数相等的情况。如果 0的个数也相等,那么设置 T = S T=S T=S 显然满足 f ( S , T , X ) = f ( S , T , Y ) f(S,T,X)=f(S,T,Y) f(S,T,X)=f(S,T,Y) 。否则, f ( S , T , X ) f(S,T,X) f(S,T,X) f ( S , T , Y ) f(S,T,Y) f(S,T,Y) 的长度不可能相等
如果 X X X Y Y Y 中的1 的个数不相等,则有 X ≠ Y X\neq Y X=Y ,而 ∣ f ( S , T , X ) ∣ = ∣ f ( S , T , Y ) ∣ |f(S,T,X)|=|f(S,T,Y)| f(S,T,X)=f(S,T,Y) 可以得到一个以 ∣ T ∣ |T| T 为单位的线性方程,从而求出 ∣ T ∣ |T| T (如果不是整数或负数,则无解)。
根据上述观察,当 X ≠ Y X\neq Y X=Y
f ( S , T , X ) = f ( S , T , Y )    ⟺    S f(S,T,X)=f(S,T,Y) \iff S f(S,T,X)=f(S,T,Y)S T T T 是长度为 g c d ( ∣ S ∣ , ∣ T ∣ ) \mathrm{gcd}(|S|,|T|) gcd(S,T) 的字符串 U U U 的重复。
因此,如果 S S S 的周期为 g c d ( ∣ S ∣ , ∣ T ∣ ) \mathrm{gcd}(|S|,|T|) gcd(S,T) ,就存在满足条件的 T T T 。这可以通过验证 S S S i i i 个字符是否等于 ( i + g c d ( ∣ S ∣ , ∣ T ∣ ) ) (i+\mathrm{gcd}(|S|,|T|)) (i+gcd(S,T)) 个字符,在 O ( ∣ S ∣ ) O(|S|) O(S) 时间内检查出来。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
int n;
string s, x, y;

LL gcd(LL a, LL b) {
    return b ? gcd(b, a % b) : a;
}

int solve() {
    cin >> s >> x >> y;
    n = s.size();
    LL cnt[2] = {};
    for (char c: x)
        cnt[c - '0']++;
    for (char c: y)
        cnt[c - '0']--;
    if (!cnt[0])
        return 1;
    if (!cnt[1])
        return 0;
    if (cnt[0] * cnt[1] > 0)
        return 0;
    cnt[0] = abs(cnt[0]), cnt[1] = abs(cnt[1]);
    if (cnt[0] * n % cnt[1])
        return 0;
    int g = gcd(n, cnt[0] * n / cnt[1]);
    for (int i = 0; i + g < n; i++)
        if (s[i + g] != s[i])
            return 0;
    return 1;
}

int main() {
    int t = 1;
    cin >> t;
    while (t--) {
        if (solve())
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

C.Row and Column Order(思维)

题意:

给你 ( 1 , 2 , … , N ) (1,2,\dots,N) (1,2,,N) 的两个排列 P = ( P 1 , P 2 , … , P N ) P=(P_1,P_2,\dots,P_N) P=(P1,P2,,PN) Q = ( Q 1 , Q 2 , … , Q N ) Q=(Q_1,Q_2,\dots,Q_N) Q=(Q1,Q2,,QN)

请在 N N N 乘以 N N N 的网格中的每个单元格中写入 01中的一个字符,以便满足以下所有条件:

  • 假设 S i S_i Si 是将 i i i / 1 1 1 行至 N N N / N N N 列中的字符连接起来得到的字符串。然后, S P 1 < S P 2 < ⋯ < S P N S_{P_1} < S_{P_2} < \dots < S_{P_N} SP1<SP2<<SPN 按词典顺序排列。
  • T i T_i Ti 是将 i i i列中的字符从 1 1 1 连接到 N N N 行得到的字符串。然后, T Q 1 < T Q 2 < ⋯ < T Q N T_{Q_1} < T_{Q_2} < \dots < T_{Q_N} TQ1<TQ2<<TQN 按词典顺序排列。

可以证明,对于任意 P P P Q Q Q ,至少有一种写法满足所有条件。

分析:

在第 P 1 P_1 P1 行的所有单元格中写入 0,在第 Q N Q_N QN 列中所有仍为空的单元格中写入 1,对于剩余的 ( N − 1 ) × ( N − 1 ) (N-1) \times (N-1) (N1)×(N1) 单元格,假设 S i ′ S'_i Si 是将第 i i i 行连接到不包括第 Q N Q_N QN 列所得到的字符串,而 T j ′ T'_j Tj 是将第 j j j 列连接到不包括第 P 1 P_1 P1 行所得到的字符串。然后,用同样的方法递归写出 S P 2 ′ < S P 3 ′ < ⋯ < S P N ′ S'_{P_2} < S'_{P_3} < \dots < S'_{P_N} SP2<SP3<<SPN T Q 2 ′ < T Q 3 ′ < ⋯ < T Q N ′ T'_{Q_2} < T'_{Q_3} < \dots < T'_{Q_N} TQ2<TQ3<<TQN

通过这种算法填充的网格将满足上述条件。除了问题中所述的条件外,它还满足条件:

  • 每列中至少有一个单元格为 0

这可以通过以下对 N N N 的归纳来证明:

  • 由于 0 总是写在第 P 1 P_1 P1 行,因此每列必须至少包含一个写有 0 的单元格。
  • P 1 P_1 P1 行不包含 1,而第 P 2 P_2 P2 行包含 1,所以 S P 1 < S P 2 S_{P_1} < S_{P_2} SP1<SP2 总是成立。
  • 除了第 P 1 P_1 P1 行之外,第 Q N Q_N QN 列包含 1,而第 Q N − 1 Q_{N-1} QN1 列除了第 P 1 P_1 P1 行之外,包含递归写有 0的单元格,因此 T Q N − 1 < T Q N T_{Q_{N-1}} < T_{Q_N} TQN1<TQN 恒成立。
  • S P i   ( 2 ≤ i ) S_{P_i}\ (2\leq i) SPi (2i) 的词序等于 S P i ′ S'_{P_i} SPi 的词序,因此 S P 2 < S P 3 < ⋯ < S P N S_{P_2} < S_{P_3} < \dots < S_{P_N} SP2<SP3<<SPN 符合递归写法。这同样适用于 T Q i   ( i ≤ N − 1 ) T_{Q_i}\ (i \leq N-1) TQi (iN1)

代码:

#include <bits/stdc++.h>

using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> p(n), q(n), tmp1(n), tmp2(n);
    for (auto &e: p)
        cin >> e;
    for (auto &e: q)
        cin >> e;
    for (int i = 0; i < n; i++) {
        tmp1[p[i] - 1] = i + 1;
        tmp2[q[i] - 1] = i + 1;
    }
    vector<vector<int>> a(n, vector<int>(n));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (tmp1[i] + tmp2[j] > n)
                a[i][j] = 1;
        }
    }
    for (auto &e: a) {
        for (auto &ans: e)
            cout << ans;
        cout << endl;
    }
    return 0;
}

D.Prefix Bubble Sort (数据结构)

题意:

给你一个 ( 1 , 2 , … , N ) (1,2,\dots,N) (1,2,,N) 的排列组合 P = ( P 1 , P 2 , … , P N ) P=(P_1,P_2,\dots,P_N) P=(P1,P2,,PN)
请考虑对这个排列进行以下运算 k   ( k = 2 , 3 , … , N ) k\ (k=2,3,\dots,N) k (k=2,3,,N)

  • 操作 k k k :对于 i = 1 , 2 , … , k − 1 i=1,2,\dots,k-1 i=1,2,,k1 这个顺序,如果是 P i > P i + 1 P_i > P_{i+1} Pi>Pi+1 ,交换 P P P i i i ( i + 1 ) (i+1) (i+1) 元素的值。

我们还给出了一个长度为 M M M 的非递减序列 A = ( A 1 , A 2 , … , A M )   ( 2 ≤ A i ≤ N ) A=(A_1,A_2,\dots,A_M)\ (2 \leq A_i \leq N) A=(A1,A2,,AM) (2AiN) 。对每个 i = 1 , 2 , … , M i=1,2,\dots,M i=1,2,,M 按此顺序进行 A 1 , A 2 , … , A i A_1, A_2, \dots, A_i A1,A2,,Ai 运算后,求 P P P 的逆序对数。

分析:

我们可以发现每次交换操作过后序列的逆序对数减一。并且由于序列 P P P单调非减,若操作 k k k使 x x x左边与它组成逆序对的数的数量减一,那么之后的每次操作的逆序对数同样也会减一。直到减到零,因此可以考虑使用树状数组维护逆序对,使用二分加差分维护操作对逆序对个数的影响。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
LL tr[N], sz, num[N], ans[N];

int lowbit(int x) { return x & -x; }

void change(int x, LL d) {
    while (x <= sz)
        tr[x] += d, x += lowbit(x);
}

LL query(int x) {
    LL res = 0;
    while (x)
        res += tr[x], x -= lowbit(x);
    return res;
}

int main() {
    int n;
    cin >> n;
    sz = n;
    vector<int> p(n);
    for (auto &x: p)
        cin >> x;
    LL tot = 0;
    for (int i = 0; i < n; ++i) {
        tot += num[i] = i - query(p[i]);
        change(p[i], 1);
    }
    int m;
    cin >> m;
    vector<int> a(m);
    for (auto &x: a)
        cin >> x;
    for (int i = 0; i < n; ++i) {
        int l = lower_bound(a.begin(), a.end(), i + 1) - a.begin();
        int r = min(l + num[i] - 1, (LL) m - 1);
        ans[l]++, ans[r + 1]--;
    }
    for (int i = 1; i < m; ++i)
        ans[i] += ans[i - 1];
    for (int i = 1; i < m; ++i)
        ans[i] += ans[i - 1];
    for (int i = 0; i < m; ++i)
        cout << tot - ans[i] << endl;
    return 0;
}

赛后交流

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值