You don't need to call m|

ForgotDream

园龄:2年4个月粉丝:12关注:47

UVA11525 Permutation

UVA11525 Permutation

题意

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

n=i=1kSi×(ki)!

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

1k50000,多测,T10

思路

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

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

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

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

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

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

代码

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

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

/**
* @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;
}

本文作者:forgot-dream

本文链接:https://www.cnblogs.com/forgot-dream/p/17167930.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ForgotDream  阅读(27)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起