康托展开
定义
求一个 \(n\) 排列的排位
正康托展开
模版
逆康托展开
模版(均为不可重复)
例题
P3014 [USACO11FEB] Cow Line S - 洛谷
查找第一个大于等于的排列
代码
| C++ |
|---|
| // 查找第一个大于等于 sum 的 f(x) 的排列
// cnt[i] 为 f(x) 中 i 的个数
vector<int> getUpper(i64 sum) {
vector<int> num;
i64 temp = sum;
while (temp) {
num.push_back(temp % 10);
temp /= 10;
}
reverse(begin(num), end(num));
array<int, 10> cur = cnt;
int pos = -1; // 最长连续能变大的数位的下标
for (int i = 0 ; i <= (int) num.size() ; i++) {
if (i < (int) num.size()) {
bool flag = false;
for (int j = num[i] + 1 ; j <= 9 ; j++) {
if (cur[j]) { // f(x) 排列中有比第 i 位大的数
flag = true;
break;
}
}
if (flag) {
pos = i; // 记录下标
}
// f(x) 排列中必须两个数都有才能替换
if (cur[num[i]] == 0) break;
cur[num[i]]--;
} else {
pos = i;
}
}
if (pos == -1) return {};
vector<int> res(num.size());
cur = cnt;
for (int i = 0 ; i < (int) num.size() ; i++) {
if (i < pos) {
res[i] = num[i];
} else if (i == pos) {
for (int j = num[i] + 1 ; j <= 9 ; j++) {
if (cur[j]) {
res[i] = j;
break;
}
}
} else {
for (int j = 0 ; j <= 9 ; j++) {
if (cur[j]) {
res[i] = j;
break;
}
}
}
cur[res[i]]--;
}
return res;
}
|
查找第一个小于等于的排列
代码
| C++ |
|---|
| // 查找第一个小于等于 sum 的 f(x) 的排列
// cnt[i] 为 f(x) 中 i 的个数
vector<int> getLower(i64 sum) {
vector<int> num;
i64 temp = sum;
while (temp) {
num.push_back(temp % 10);
temp /= 10;
}
reverse(begin(num), end(num));
array<int, 10> cur = cnt;
int pos = -1; // 最长连续能变小的数位的下标
for (int i = 0 ; i <= (int) num.size() ; i++) {
if (i < (int) num.size()) {
bool flag = false;
for (int j = num[i] - 1 ; j >= 0 ; j--) {
if (cur[j]) { // f(x) 排列中有比第 i 位小的数
flag = true;
break;
}
}
if (flag) {
pos = i; // 记录下标
}
// f(x) 排列中必须两个数都有才能替换
if (cur[num[i]] == 0) break;
cur[num[i]]--;
} else {
pos = i;
}
}
if (pos == -1) return {};
vector<int> res(num.size());
cur = cnt;
for (int i = 0 ; i < (int) num.size() ; i++) {
if (i < pos) {
res[i] = num[i];
} else if (i == pos) {
for (int j = num[i] - 1 ; j >= 0 ; j--) {
if (cur[j]) {
res[i] = j;
break;
}
}
} else {
for (int j = 9 ; j >= 0 ; j--) {
if (cur[j]) {
res[i] = j;
break;
}
}
}
cur[res[i]]--;
}
return res;
}
|