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

浙公网安备 33010602011771号