1. 程式人生 > 實用技巧 >CF1464D The Thorny Path

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;
}