uva11525 - Permutation 线段树加速康托展开

本文探讨了使用康托展开解决排列问题的方法,通过输入序列计算特定排列位置,并实现高效算法。

Problem F
Permutation
Input:
Standard Input

Output: StandardOutput

 

Given N and K find the N’thpermutation of the integers from 1 to K when those permutations arelexicographically ordered.  N startsfrom 0. Since N is very large N will be represented by a sequence of Knon-negative integers S1, S2 ,…, Sk. From thissequence of integers N can be calculated with the following expression.

 

 

Input

First line of the input containsT(≤10) the number of test cases. Each of these test cases consists of 2lines. First line contains a integer K(1≤K≤50000). Next linecontains K integers S1, S2 ,…, Sk.(0≤Si≤K-i).

 

Output

For each test case output contains N’th permutation ofthe integers from 1 to K. These K integers should be separated by a singlespace.

 

SampleInput                           Output for Sample Input

4
3
2 1 0
3
1 0 0
4
2 1 1 0
4
1 2 1 0

 

3 2 1
2 1 3
3 2 4 1
2 4 3 1

 

 康托展开:点击打开链接

 才发现原来写的八数码建立的表就是康托展开。。

  简单的说就是每位找出1-K中之前没出现的数并且比这位数小的有多少个,这个个数乘以排列在这个数后面的数的个数的阶乘。康托展开的数就是排列在全排列中的位置,和原来的排列是一一对映的关系。

  题目已经给了s1...sn,每次只要找1-K中之前没出现的第si小的数(从0开始),就知道第i位是什么了。如果每位都循环找一遍时间太长,用线段树维护当前区间没有出现的数的个数。如果左边剩下的个数不够si,就往右边找si-左边剩下的个数。

#include<cstring>
#include<cstdio>
#include<iostream>
#include<climits>
#include<cmath>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
#define MAXN 50010
#define MAXNODE 4*MAXN
using namespace std;
int T,K,cnt[MAXNODE];
void build(int o,int L,int R){
    cnt[o]=R-L+1;
    if(L==R) return;
    int lc=(o<<1),rc=(o<<1|1),mid=(L+R)/2;
    build(lc,L,mid);
    build(rc,mid+1,R);
}
int query(int o,int L,int R,int s){
    cnt[o]--;
    if(L==R) return L;
    int lc=(o<<1),rc=(o<<1|1),mid=(L+R)/2;
    if(cnt[lc]>s) return query(lc,L,mid,s);     //注意编号从0开始,所以左边剩余的个数要大于s
    else return query(rc,mid+1,R,s-cnt[lc]);
}
int main(){
    freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d",&K);
        build(1,1,K);
        for(int i=0;i<K;i++){
            int s;
            scanf("%d",&s);
            if(i) printf(" ");
            printf("%d",query(1,1,K,s));
        }
        puts("");
    }
    return 0;
}




非常好的问题! ### ❌ `next_permutation` **不是** 康托展开。 它们是两种完全不同的思想,虽然都和“全排列的字典序”有关,但用途、原理和复杂度完全不同。 --- ## ✅ 一、核心区别总结 | 特性 | `next_permutation` | 康托展开(Cantor Expansion) | |------|--------------------|-------------------------------| | 类型 | STL 算法函数 | 数学公式/编码方法 | | 功能 | 生成下一个字典序排列 | 计算某个排列的字典序排名(或反推第k个排列) | | 时间复杂度 | 单次 $O(N)$ | $O(N^2)$ 或 $O(N \log N)$(带树状数组优化) | | 是否需要阶乘? | 否 | 是(预处理 $0!, 1!, ..., (n-1)!$) | | 适用场景 | 小 $M$ 的“后移 M 步”问题 | 快速跳转到第 $k$ 个排列(如 $k=10^9$) | | 能否处理大 $N$? | ✅ 可以(只要 M 小) | ❌ $N > 20$ 时阶乘太大无法存储 | --- ## ✅ 二、详细解释 ### 🔹 1. `next_permutation`:一步一步走 它是一个**迭代算法**,只关心:“当前排列的下一个是谁?” #### 原理步骤: 1. 从右往左找第一个位置 `i`,使得 `a[i] < a[i+1]` 2. 再从右往左找第一个 `j`,使得 `a[j] > a[i]` 3. 交换 `a[i]` 和 `a[j]` 4. 反转 `a[i+1 ... end]` > 这样得到的就是字典序中紧接的下一个排列。 #### 示例: ```cpp [1,2,3,4,5] → [1,2,3,5,4] → [1,2,4,3,5] → ... ``` 每次调用就是“走一步”。 ✅ 优点:简单高效,适合 $M=100$ 这种小步数移动。 ❌ 缺点:不能直接跳 $10^9$ 步。 --- ### 🔹 2. 康托展开:数学公式直接定位 康托展开是一种**排列编码系统**,它可以: > 把一个排列映射成一个整数(它的字典序排名) 公式如下: $$ \text{rank} = 1 + \sum_{i=1}^{n} d_i \times (n-i)! $$ 其中 $d_i$ 表示在第 $i$ 位右边且比 $a_i$ 小的数字个数。 #### 举例: 排列 `[2,1,3]` 在所有 3 元排列中排第 3。 - 第1位是2,后面有1个比它小的 → $1 \times 2! = 2$ - 第2位是1,后面没有更小的 → $0 \times 1! = 0$ - 第3位是3 → 不参与 - 总计:$2 + 0 + 1 = 3$ 👉 所以它是第3个排列。 #### 逆康托展开: 反过来也可以由排名 $k$ 构造出对应的排列。 ✅ 优点:可以快速求第 $k$ 个排列,比如 $k = 10^9$ ❌ 缺点:当 $n > 20$ 时,$(n-1)!$ 太大,long long 存不下! --- ## ✅ 三、对比应用:解决同一道题的不同方式 假设我们要解决 P1088,给定排列 A,求它之后第 M 个排列。 | 方法 | 如何做 | 限制 | |------|--------|-------| | `next_permutation` × M 次 | 一步步走 M 步 | 要求 M 很小(≤100) | | 康托展开 + 逆康托展开 | 先算当前排名 → 加 M → 再生成新排列 | 要求 N ≤ 20(否则阶乘溢出) | > ⚠️ 所以对于本题 $N \leq 10000, M \leq 100$,**只能用 `next_permutation`,不能用康托展开!** --- ## ✅ 四、形象比喻 | 比喻 | 解释 | |------|------| | `next_permutation` | 像一个人走路:一步一步往前走,每步都知道下一步在哪 | | 康托展开 | 像 GPS 定位:我知道我现在在第几个位置,也能直接导航到第 $k$ 个位置 | 你想从第 100 个排列走到第 103 个? - `next_permutation`:走 3 步 ✔️ - 康托展开:先算自己在哪 → 加 3 → 重建目标排列 ❌(但 N 太大会失败) --- ## ✅ 总结回答你的问题: > ❓ `next_permutation` 是康托展开吗? ### ❌ 不是! - `next_permutation` 是一个**构造性算法**,用于生成下一个排列; - 康托展开是一个**数学编码方法**,用于计算排列的排名; - 两者目的不同,实现方式不同,适用范围也不同; - 在本题中,由于 $N$ 太大,**我们只能使用 `next_permutation`,而无法使用康托展开**。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值