【构造】0603 Lucky Permutation

题意:

给定一个长度为 n n n 的排列 p p p,对于 1 ≤ i < j ≤ n 1\leq i1i<jn,交换 p i p_i pi p j p_j pj 为一次操作,问至少要多少次操作,可以使得最终的排列的逆序对数量为 1 1 1

数据范围: 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105

题解:

首先考虑将 p p p 转换为逆序对为 0 0 0 的排列,即 [ 1 , 2 , ⋯   , n − 1 , n ] [1,2,\cdots,n-1,n] [1,2,,n1,n]

对于排列 p p p ,可以构成 c c c 个环, i i i 所在的环为: [ i , p i , p p i , p p p i , . . . ] [i,p_i,p_{p_i},p_{p_{p_i}},...] [i,pi,ppi,pppi,...] 。如此,将 p p p 转换为 [ 1 , 2 , ⋯   , n − 1 , n ] [1,2,\cdots,n-1,n] [1,2,,n1,n] 需要的最少次数是多少呢?

  • 考虑长度为 1 1 1 的环,即 p i = i p_i=i pi=i,此时无需交换,答案为 0 0 0
  • 考虑长度为 2 2 2 的环,即 p i = j , p j = i p_i=j,p_j=i pi=j,pj=i,此时仅需交换一次,答案为 1 1 1
  • 考虑长度为 3 3 3 的环,即 p i = j , p j = k , p k = i p_i=j,p_j=k,p_k=i pi=j,pj=k,pk=i,此时第一次将 j j j k k k 交换,变为 p i = k , p j = j , p k = i p_i=k,p_j=j,p_k=i pi=k,pj=j,pk=i,然后第二次将 i i i k k k 交换,变为 p i = i , p j = j , p k = k p_i=i,p_j=j,p_k=k pi=i,pj=j,pk=k,此时需要交换两次,答案为 2 2 2
  • 考虑长度为 l e n len len 的环,考虑交换到最后,必然是一次交换使得两个值都到了正确的位置,之前的交换只会使得一个值到达正确的位置。故此时需要交换 l e n − 1 len-1 len1 次,答案为 l e n − 1 len-1 len1

故对于每个长度为 l e n i len_i leni 的环,交换次数为 l e n i − 1 len_i-1 leni1,共有 c c c 个环,故总交换次数为: ∑ i = 1 c ( l e n i − 1 ) = ( ∑ i = 1 c l e n i ) − c = n − c \sum\limits_{i=1}^c (len_i-1)=(\sum\limits_{i=1}^clen_i)-c=n-c i=1c(leni1)=(i=1cleni)c=nc

接下来我们考虑将 p p p 转换为逆序对数量为 1 1 1 的排列。

上面已经处理得到了将 p p p 转换为 [ 1 , 2 , ⋯   , n − 1 , n ] [1,2,\cdots,n-1,n] [1,2,,n1,n] 的环的数量,以及每个环中的数。

一共有 n − 1 n-1 n1 种逆序对数量为 1 1 1 的排列,即:

  • [ 2 , 1 , 3 , . . . , n − 1 , n ] [2,1,3,...,n-1,n] [2,1,3,...,n1,n]
  • [ 1 , 3 , 2 , . . . , n − 1 , n ] [1,3,2,...,n-1,n] [1,3,2,...,n1,n]
  • [ 1 , 2 , 4 , 3 , . . . , n − 1 , n ] [1,2,4,3,...,n-1,n] [1,2,4,3,...,n1,n]
  • [ 1 , 2 , 3 , . . . , n , n − 1 ] [1,2,3,...,n,n-1] [1,2,3,...,n,n1]

考虑将排列 p p p 转换为排列 ① 。

  • 如果 1 1 1 2 2 2 p p p 中不在一个环上,则交换后, 1 1 1 2 2 2 所在的环会合并成一个环。
  • 如果 2 2 2 1 1 1 p p p 中在一个环上,则交换后, 2 2 2 1 1 1 所在的环会分成两个环。

对于其余的逆序对数量为 1 1 1 的排列是一样的。

证明:
举个 1 1 1

1 2 3 4 5 6 7 8
2 3 4 1 5 6 7 8

1->2->3->4->1

对于 环 1->2->3->4->1,交换 1 1 1 2 2 2
交换如下:

2 1 3 4 5 6 7 8
2 3 4 1 5 6 7 8

2->2

1->3->4->1

交换后一个环 1->2->3->4->1 变成两个环,环 2->2 和环 1->3->4->1

举个 2 2 2

1 2 3 4
1 2 3 4

1->1
2->2
3->3
4->4

对于 环 1->12->2,交换 1 1 1 2 2 2
交换如下:

1 2 3 4
2 1 3 4

1->2->1
3->3
4->4

交换后两个环 1->12->2 变成一个环 1->2->1

故可以得出

  • 如果两个数在 p p p 中在一个环上,那么交换后其必然不在一个环上,即一个环分裂为两个环。
  • 如果两个数在 p p p 中不在一个环上,那么交换后其必然在一个环上,即两个环分裂为一个环。

来考虑一个单纯的数交换问题:

1 2 3 4
2 3 4 1

1. 1->2->3->4->1

如果交换 1 1 1 3 3 3
交换后为:

1 2 3 4
2 1 4 3

1->2->1
3->4->3

可以发现,交换的点 1 1 1 和点 3 3 3 的指向未变,而指向点 1 1 1 4 4 4 和指向点 3 3 3 2 2 2 的方向分别交换,即 2->3 变成了 2->14->1 变成了 4->3

再来考虑一个例子:

1 2 3 4
1 3 4 2

1->1
2->3->4->2

如果交换 1 1 1 2 2 2
交换后为:

1 2 3 4
2 3 4 1

1->2->3->4->1

可以发现,交换前后, 2 2 2 的指向未变, 1 1 1 的指向好像变成了 2 2 2 ,但要清楚,1->1 这个环可以看出一个 1(1)->1(2)1(2)->1(1) 两条边。一方面 1 1 1 作为入度点,一方面 1 1 1 作为出度点。

可以看到环的最后, 1 1 1 仍然指向 1 1 1 ,但是 1 1 1 也指向 2 2 2。故可以像上述将 1 1 1 拆点后来看,1(1)->2,4->1(2),1(2)->1(1)

再来思考:

  • 交换前指向 2 2 2 4 4 4 ,交换后指向了 1(2) 2 2 2 的指向不变。
  • 交换前指向 1(1)1(2) ,交换后指向了 2 2 21(2) 的指向不变,仍然指向 1(1)

故两个环变成了一个环。

故得证:交换的 i i i j j j p p p 中若在同一环上,交换后裂成两个环,交换的 i i i j j j p p p 中若在两个环上,交换后合并为一个环。

代码:

#include 
using namespace std;

void solve() {
    int n;
    cin >> n;

    vector<int> a(n + 1), group(n + 1);
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    int circle = 0;
    for (int i = 1; i <= n; ++i) {
        if (group[i] != 0) continue;
        circle += 1;
        int j = i;
        while (!group[j]) {
            group[j] = i;
            j = a[j];
        }
    }

    int ans = n + 1;
    for (int i = 1; i < n; ++i) {
        if (group[i] == group[i + 1]) {
            ans = min(ans, n - (circle + 1));
        } else {
            ans = min(ans, n - (circle - 1));
        }
    }

    cout << ans << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T = 1;
    cin >> T;
    while (T--) solve();

    return 0;
}

你可能感兴趣的:(#,构造题,c++,算法,开发语言)