AtCoder Regular Contest 181 A~D

A.Sort Left and Right(思维)

题意:

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

你想通过执行下面的操作零次或多次来满足所有 i=1,2,…,Ni=1,2,\dots,Ni=1,2,,NPi=iP_i=iPi=i 要求:

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

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

分析:

分类讨论:

  • 若排列已经是升序:操作次数为000
  • [1,i][1,i][1,i]已经是1−i1-i1i的排列,操作次数为111
  • P1≠nP_1 \neq nP1=n或者Pn≠1P_n \neq 1Pn=1操作次数为222
  • 剩余情况操作次数为333

代码:

#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(数学)

题意:

对于由小写英文字母组成的字符串 SSSTTT 以及由 01 组成的字符串 XXX ,请定义由小写英文字母组成的字符串 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 ,如果 XXX 的第 iii 个字符是 0,则将 SSS 追加到尾部,如果是 1,则将 TTT 追加到尾部。

给你一个由小写英文字母组成的字符串 SSS 以及由01组成的字符串 XXXYYY

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

分析:

首先,考虑 XXXYYY1 的个数相等的情况。如果 0的个数也相等,那么设置 T=ST=ST=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) 的长度不可能相等
如果 XXXYYY 中的1 的个数不相等,则有 X≠YX\neq YX=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≠YX\neq YX=Y
f(S,T,X)=f(S,T,Y)  ⟺  Sf(S,T,X)=f(S,T,Y) \iff Sf(S,T,X)=f(S,T,Y)STTT 是长度为 gcd(∣S∣,∣T∣)\mathrm{gcd}(|S|,|T|)gcd(S,T) 的字符串 UUU 的重复。
因此,如果 SSS 的周期为 gcd(∣S∣,∣T∣)\mathrm{gcd}(|S|,|T|)gcd(S,T) ,就存在满足条件的 TTT 。这可以通过验证 SSSiii 个字符是否等于 (i+gcd(∣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=(P1,P2,…,PN)P=(P_1,P_2,\dots,P_N)P=(P1,P2,,PN)Q=(Q1,Q2,…,QN)Q=(Q_1,Q_2,\dots,Q_N)Q=(Q1,Q2,,QN)

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

  • 假设 SiS_iSi 是将 iii / 111 行至 NNN / NNN 列中的字符连接起来得到的字符串。然后, SP1<SP2<⋯<SPNS_{P_1} < S_{P_2} < \dots < S_{P_N}SP1<SP2<<SPN 按词典顺序排列。
  • TiT_iTi 是将 iii列中的字符从 111 连接到 NNN 行得到的字符串。然后, TQ1<TQ2<⋯<TQNT_{Q_1} < T_{Q_2} < \dots < T_{Q_N}TQ1<TQ2<<TQN 按词典顺序排列。

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

分析:

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

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

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

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

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

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

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

分析:

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

代码:

#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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值