UVA11525 Permutation

UVA11525 Permutation

题意

给你一个正整数 \(k\) 与一个序列 \(S\)。定义 \(n\) 如下:

\[n = \sum_{i = 1}^{k} S_i \times (k - i)! \]

你要输出长度为 \(k\) 的字典序为 \(n\) 的排列,排列从 \(0\) 开始编号。

\(1 \le k \le 50000\),多测,\(T \le 10\)

思路

如果你曾经听说过康托展开的话,那么恭喜你,你又会了一道水蓝

如果没有的话,也不要紧,现在就去做模板题 P5367 吧!那里题解区的老哥讲的比我清楚的多

做完模板题后,再来看这个题,你会发现,题目给出的这个式子是不是跟康托展开的式子有点像?

没错,题目给出的这个 \(n\) 值,事实上就是待求序列的康托展开值。而你要做的,是把这个序列还原出来,也就是所谓的逆康托展开。

这当然也是简单的,如果你已经深刻理解了康托展开的具体过程,那么不难发现题目中的 \(S_i\) 是这一项的数,在删掉了前边找出的数后,在 \(1 \sim k\) 的序列中的排名。

可能听上去有些拗口,不过看了代码,就会明白的。

代码

时间复杂度是 \(O(k ^ 2)\)?但是我过了,这说明 STL 数据结构在吸了氧之后是相当快的。

当然是可以用权值线段树或者平衡树做到 \(O(k \log k)\) 的,不过能过就行。

/**
 * @file    UVA11525 Permutation.cpp
 * @author  ForgotDream
 * @brief   逆康托展开
 * @date    2023-02-28
 */
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
  int k;
  std::cin >> k;
  
  std::vector<int> s(k), rest(k);
  for (auto &i : s) {
    std::cin >> i;
  }
  std::iota(rest.begin(), rest.end(), 1);  
  // 这一句的作用是生成一个递增的 1 ~ n 的序列

  std::vector<int> ans;

  for (int i = 0; i < k; i++) {
    ans.push_back(rest[s[i]]);  // 找到当前项的数字
    rest.erase(rest.begin() + s[i]);  // 删去这一项
  }

  for (int i = 0; i < k; i++) {
    std::cout << ans[i] << " \n"[i == k - 1];
  }

  return;
}

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

  int T;
  std::cin >> T;

  while (T--) {
    solve();
  }

  return 0;
}
posted @ 2023-03-01 13:52  ForgotDream  阅读(38)  评论(0)    收藏  举报