CF1464D The Thorny Path

link

总的方案数是 \(\prod c_i\)\(c_i\) 是置换环大小。

可以发现对于一个置换环 \(p\),交换 \(p_i, p_j\) 会使其分裂成大小为 \(|i - j|\)\(j\) 的环。交换两个不同环上的元素,会使两个环合并。因此我们可以在一次操作内使某个置换环分裂成任意大小,或合并两个环。

注意到对于确定的和 \(n\),我们要尽可能得到大小为 \(3\) 的环。对于 \(3 \mid n\),所有的环大小应该都为 \(3\),对于 \(n \equiv 1 \pmod 3\),会多出一个 \(4\)\(2 + 2\),对于 \(n \equiv 2\pmod 3\),会多出一个 \(2\)

接下来计算最小操作步数:

对于 \(3 \mid n\),可以直接贪心地分裂出大小为 \(3\) 的环,剩下一些 \(1, 2\) 直接合并。

对于 \(3 \nmid n\),注意到最多需要额外取出一个 \(4\),如果 \(n \ge 7\),那么取出一个 \(3\) 是不劣的。

现在环的大小在 \([1, 6]\) 之内,那么就不需要进行复杂的讨论了,我们可以直接枚举 \(2\)\(4\) 的所有整数拆分,递归地枚举从 \([1, 6]\) 中哪个数拆分出某个数即可。

#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 1000005, M = 1000000007;
const int INF = 1000000000;

int p[N], pw3[N / 2];
bool vis[N];
int cnt[10];
int opt;

int check() {
  int c1 = cnt[1] + cnt[4], c2 = cnt[2] + cnt[5];
  return cnt[4] + cnt[5] + cnt[6] + (c1 < c2 ? c2 : c2 + (c1 - c2) / 3 * 2);
}
template<class ...Args>
int check(int x, Args... args) {
  int res = INF;
  for (int i = x; i <= 6; ++i) {
    if (cnt[i] == 0) continue;
    --cnt[i]; ++cnt[i - x];
    res = min(res, check(args...) + (i > x));
    ++cnt[i]; --cnt[i - x];
  }
  return res;
}

int main() {
  pw3[0] = 1;
  for (int i = 1; i <= 500000; ++i) pw3[i] = (pw3[i - 1] * 3LL) % M;
  int T;
  for (scanf("%d", &T); T; --T) {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) scanf("%d", p + i);
    int c3 = n % 3 == 1 ? n / 3 - 1 : n / 3;
    printf("%lld ", (1LL * pw3[c3] * (n % 3 == 0 ? 1 : n - 3 * c3) % M));

    fill(vis + 1, vis + 1 + n, false);
    fill(cnt, cnt + 7, 0);
    int res = 0, opt = INF;
    for (int i = 1; i <= n; ++i) {
      if (vis[i]) continue;
      int sz = 1, x = i;
      vis[i] = true;
      while (!vis[p[x]]) x = p[x], vis[x] = true, ++sz;
      while (sz >= 7) sz -= 3, ++res;
      ++cnt[sz];
    }

    if (n % 3 == 0)
      opt = check();
    else if (n % 3 == 1) {
      opt = min(opt, check(1, 1, 1, 1) + 2);
      opt = min(opt, check(1, 1, 2) + 1);
      opt = min(opt, check(2, 2));
      opt = min(opt, check(1, 3) + 1);
      opt = min(opt, check(4));
    }
    else {
      opt = min(opt, check(1, 1) + 1);
      opt = min(opt, check(2));
    }

    printf("%d\n", res + opt);
  }
  return 0;
}
posted @ 2020-12-22 13:03  RiverHamster  阅读(139)  评论(0编辑  收藏  举报
\