2023.1.6 (Codeforces Round #842 (Div. 2))

A.Greatest Convex

https://codeforces.com/contest/1768/problem/A

Description

求出最大的 \(x (1 \leq x < k)\),使得 \(x! + (x - 1)!\)\(k\) 的倍数。

Solution

\[x! + (x - 1)! = (x + 1) \cdot (x - 1)! \]

\(x = k - 1\)时,\((x + 1) \cdot (x - 1)! = k \cdot (k - 2)!\),该值始终为 \(k\) 的倍数。故输出 \(k - 1\)即可。

Code

https://codeforces.com/contest/1768/submission/188069238

B.Quick Sort

https://codeforces.com/contest/1768/problem/B

Description

定义一种操作:对一个长度为 \(n\) 的排列,每次可以从排列中任意选出 \(m\) 个数(将这些数从原排列中删除),将他们按升序排列后加到原排列末尾。最小化使得这个长度为 \(n\) 的排列整体升序的操作次数。

Solution

需要发现,以 \(1\) 为开头的、连续的上升序列,这些数字是不用选中的,其余的都需要选。直接计算 \(\lceil \frac {n - length} {m} \rceil\) 即可。

Code

#include <bits/stdc++.h>
const int N = 2e5 + 5;
using namespace std;
int T, n, m, a[N];
int main() {
    ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	freopen("in","r", stdin);
    cin >> T;
    while (T--) {
        int cnt = 0;
        cin >> n >> m;
        for (int i = 1; i <= n; i++) cin >> a[i];
        int now = 1;
        for (int i = 1; i <= n; i++) {
            if (a[i] == now) now++;
        }
        cout << (n - now + 1 + m - 1) / m << endl;
    }
	return 0;
}

C. Elemental Decompress

https://codeforces.com/contest/1768/problem/C

Description

给定一个序列 \(a_n\),询问是否存在两个长度为 \(n\) 的排列 \(p_n,q_n\),使得 \(a_i = max(p_i, q_i), 1 \leq i \leq n\)。若存在,需输出方案。

Solution

出现以下任意一种情况都是无解的:\(a_n\)中未出现过\(n\)\(a_n\)\(1\)的出现次数大于\(1\)\(a_n\)中任意一个数字的出现次数大于\(2\)

确定有解后,先把那些一定要填的数字填好,剩余数字结合贪心思路,每次能往大里填(结合\(lower\)_\(bound\)即可)。

Code

#include <bits/stdc++.h>
const int N = 2e5 + 5;
using namespace std;

int T, n, cnt[N], a[N], ans1[N], ans2[N];
bool vis1[N], vis2[N];
vector<int> fi, se;

int main() {
    ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
    freopen("in","r", stdin);
    cin >> T;
    while (T--) {
        bool flag = false;
        cin >> n;
        for (int i = 1; i <= n; i++) cnt[i] = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            cnt[a[i]]++;
            if (cnt[a[i]] > 2) flag = true;
        }
        flag |= (cnt[1] == 2);
        flag |= (!cnt[n]);
        if (flag) cout << "NO" << endl;
        else {
            fi.clear();
            se.clear();
            for (int i = 1; i <= n; i++) {
                vis1[i] = vis2[i] = false;
                ans1[i] = ans2[i] = 0;
            }
            for (int i = 1; i <= n; i++) {
                if (!vis1[a[i]]) {
                    vis1[a[i]] = true;
                    ans1[i] = a[i];
                }
                else {
                    ans2[i] = a[i];
                    vis2[a[i]] = true;
                }
                if (a[i] == 1) {
                    vis1[1] = vis2[1] = true;
                    ans1[i] = ans2[i] = 1;
                }
            }
            for (int i = n; i >= 1; i--) {
                if (!vis1[i]) se.push_back(i);
                if (!vis2[i]) fi.push_back(i);
            }
            for (int i = 1; i <= n; i++) {
                if (!ans1[i]) {
                    auto it = lower_bound(se.begin(), se.end(), ans2[i], greater<int>());
                    if (it == se.end()) {
                        flag = true;
                        break;
                    }
                    ans1[i] = se[it - se.begin()];
                    se.erase(it);
                }
            }
            for (int i = 1; i <= n; i++) {
                if (!ans2[i]) {
                    auto it = lower_bound(fi.begin(), fi.end(), ans1[i], greater<int>());
                    if (it == fi.end()) {
                        flag = true;
                        break;
                    }
                    ans2[i] = fi[it - fi.begin()];
                    fi.erase(it);
                }
            }
            if (flag) cout << "NO" << endl;
            else {
                cout << "YES" << endl;
                for (int i = 1; i <= n; i++) {
                    cout << ans1[i];
                    i == n ? cout << endl : cout << " ";
                }
                for (int i = 1; i <= n; i++) {
                    cout << ans2[i];
                    i == n ? cout << endl : cout << " ";
                }
            }
        }
    }
	return 0;
}

D.Lucky Permutation

https://codeforces.com/contest/1768/problem/D

Description

定义一种操作:对一个长度为 \(n\) 的排列 \(a_n\),每次可以从排列中选出 \(i\)\(j\) ,交换 \(a_i\)\(a_j\)。最小化使得最终的 \(a_n\) 有且仅有一个逆序对的操作次数。

Solution

对于每个 \(i\),若考虑从 \(a_i\)\(i\) 连一条有向边,根据排列的性质(每个点的入度和出度都为 \(1\)),则会生成 \(x\) 个环 \((1 \leq x \leq n)\)

不难发现,最终状态一定是「一个升序排列,且选择一对相邻的数交换后」的形式。若按之前的图的形式表示,则可以表示为 \(n - 1\) 个自环 + \(1\) 个长度为 \(2\) 的环(且这个长度为 \(2\) 的环,是由两个相邻的数字构成的)。

对于原排列表示出的 \(x\) 个环,自环可以不管,大环则需要被拆解成小环和自环。当交换两个数有效时,可以等效为「大环变小,且新增自环」。若存在一个大环其中包含两个相邻的数,那么意味着在拆解的过程中,可以把他们俩留下,其他变为自环。否则在所有大环拆成自环后,还需要选择两个相邻的自环,将他们变成长度为 \(2\) 的环。

利用并查集可以找出环的个数后,再在每个环中判断,是否存在相邻元素。若存在则答案 \(-1\),不存在则答案 \(+1\)

Code

#include <bits/stdc++.h>
const int N = 2e5 + 5;
using namespace std;

int T, n, x, fa[N];
set<int> s, son[N];

int get_fa(int x) { return x == fa[x] ? x : fa[x] = get_fa(fa[x]); }

int main() {
    ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	freopen("in","r", stdin);
    cin >> T;
    while (T--) {
        bool flag = false;
        cin >> n;
        for (int i = 1; i <= n; i++) fa[i] = i;
        for (int i = 1; i <= n; i++) {
            cin >> x; 
            int fx = get_fa(i), fy = get_fa(x);
            fa[fy] = fx;
        }
        for (int i = 1; i <= n; i++) {
            int x = fa[i];
            while (x != fa[x]) x = fa[x];
            s.insert(x);
            son[x].insert(i);
        }
        for (int i = 1; i <= n; i++) {
            int last = -1;
            for (auto x : son[i]) {
                if (last != -1 && x - last == 1) {
                    flag = true;
                    break;
                }
                last = x;
            }
            if (flag) break;
        }
        cout << n - (int)s.size() + (!flag ? 1 : -1) << endl;
        s.clear();
        for (int i = 1; i <= n; i++) son[i].clear();
    }
	return 0;
}

posted @ 2023-01-07 01:18  BeyondLimits  阅读(26)  评论(0编辑  收藏  举报