AT_wtf22_day1_b Non-Overlapping Swaps 题解

特别有意思的题。

思路#

首先,由于它的限制次数还算比较多。

在我们正常最少交换次数时,我们是通过提取每一个置换环,使用置换环长度减一之和来达到的。

那么在这道题中,我们依然可以继续这么考虑,每一次交换一个置换环,然后操作一次 (1,1) 进行刷新。

如何达到置换环长度减一?

我们现在称一个点 x 的标号是它在原序列中的位置 x,前驱是满足 Pi=xi,后继是 Px

我们考虑将这个置换环上的点变成一个序列。

它在置换环上的位置是在序列上的位置。

然后我们以它的标号建一个小根笛卡尔树。

这个笛卡尔树有什么性质?

树上的每一个点都想要到达它在先序遍历下的后一个点。

那么很容易想到。

我们按后序遍历每一次交换这个点与它的父亲(跳过根)。

这样就成功了。

这个最后是正确的很好说明,自己稍微画一画就发现了。

至于操作的合法性:

  1. 对于一个有左儿子的点来说,操作完它后会操作它的左子树,这里面的所有点显然小于这个点。
  2. 对于一个没有左儿子但是有爷爷的点来说,操作完它会操作它的父亲,这显然也是合法的。
  3. 对于一个两者都没有的点,那么这个点显然是根的右儿子,因为当我们从小到大的枚举每一个置换环时,一个环的根一定也是这个环的第一个数,所以根没有左儿子,那么这就是最后一次操作,所以也是合法的。

代码就很好写了。

时间复杂度:O(n)

Code#

#include <bits/stdc++.h>
using namespace std;

int n;
int p[250010], v[250010];
int a[250010], f[250010], s[250010];

inline void solve() {
  cin >> n;
  for (int i = 1; i <= n; i++) cin >> p[i];
  for (int i = 1; i <= n; i++) v[i] = f[i] = 0;
  vector<pair<int, int>> ns;
  for (int i = 1; i <= n; i++) {
    if (v[i]) continue;
    int ct = 0;
    int tp = 0;
    a[++ct] = i;
    for (int j = p[i]; j != i; j = p[j]) a[++ct] = j;
    s[++tp] = 1;
    for (int j = 2; j <= ct; j++) {
      int las = 0;
      while (a[s[tp]] > a[j]) f[las] = s[tp], las = s[tp--];
      f[las] = s[++tp] = j;
    }
    for (int i = 2; i <= tp; i++) f[s[i]] = s[i - 1];
    if (i != 1) ns.emplace_back(1, 1);
    for (int i = ct; i >= 1; i--)
      if (f[i]) ns.emplace_back(minmax(a[i], a[f[i]]));
    for (int i = 1; i <= ct; i++) v[a[i]] = 1;
  }
  cout << ns.size() << "\n";
  for (auto i : ns)
    cout << i.first << " " << i.second << "\n";
}

int main() {
  int t;
  cin >> t;
  while (t--) solve();
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/18738497

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示