Lucky Permutation

Lucky Permutation

Created by LXC on Sun Apr 7 19:39:02 2024

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

ranting: 1800

tag: constructive algorithms, dfs and similar, graphs, greedy

problem

给定整数 $n$ 和一个 $1\sim n$ 的排列 $p$。
你可以对排列 $p$ 进行下列操作任意次:

  • 选择整数 $i,j(1\leq i<j\leq n)$,然后交换 $p_i,p_j$ 的值。

你需要求出至少需要进行上述操作多少次才能使 $p$ 恰有一个逆序对。
每个测试点包含 $t$ 组数据。

输入格式

第一行输入一个整数 $t(1\leq t\leq10^4)$ 表示数据组数,接下来对于每组数据:
第一行输入一个整数 $n(2\leq n,\sum n\leq2\times10^5)$。
接下来输入一行 $n$ 个整数 $p_1,p_2,\cdots,p_n(1\leq p_i\leq n)$,保证 $p$ 是一个 $1\sim n$ 的排列。

输出格式

对于每组数据:
输出一行一个整数表示使 $p$ 恰有一个逆序对所需的最小操作次数。
可以证明一定存在操作方案使得 $p$ 恰有一个逆序对。

solution

建图找环,对于任意位置i,我们向$p_i$位置指向一条有向边。

那么一个环内各个数都不在原本位置上,现在让他们全部回到原来位置上,只需要环上点的个数-1次交换操作。

如果我们把所有元素归位了那么就是没有逆序对了

现在需要让排列恰好有一个逆序对,这个逆序对,实际上是一个升序排列中某相邻元素交换了位置。

如果之前的环存在有相邻元素,那么实际上,这一步可以省去,否则我们最后还需要交换一步。

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

#include <bits/stdc++.h>
// #define SINGLE_INPUT
#define ll long long
#define ull unsigned long long
#define N 500005
#define MOD 998244353
using namespace std;

random_device seed;
ranlux48 engine(seed());
int random(int l, int r) {
uniform_int_distribution<> distrib(l, r);
return distrib(engine);
}
template<class t,class u> ostream& operator<<(ostream& os,const pair<t,u>& p) {
return os<<'['<<p.first<<", "<<p.second<<']';
}
template<class t> ostream& operator<<(ostream& os,const vector<t>& v) {
os<<'['; int s = 1;
for(auto e:v) { if (s) s = 0; else os << ", "; os << e; }
return os<<']';
}
template<class t,class u> ostream& operator<<(ostream& os,const map<t,u>& mp){
os<<'{'; int s = 1;
for(auto [x,y]:mp) { if (s) s = 0; else os << ", "; os<<x<<": "<<y; }
return os<<'}';
}

void sol() {
int n;
cin >> n;
vector<int> a(n), vis(n);
for (int& i:a) cin >> i, --i;
int add = 1, ans = 0;
for (int i=0, id=1; i<n; i++) {
if (vis[i]) continue;
int j = i;
vis[j] = id;
while (vis[a[j]] == 0) {
ans++;
vis[j = a[j]] = id;
if(add && (j>0 && vis[j] == vis[j-1] || j+1<n && vis[j] == vis[j+1])) {
add = 0;
ans--;
}
}
id++;
}
cout << ans+add << "\n";
}

int main() {
cout << setprecision(15) << fixed;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifndef SINGLE_INPUT
int t;
cin >> t;
while (t--) {
sol();
}
#else
sol();
#endif
return 0;
}