UVA 11525

(一种通过康托展开求排列的方法)

Problem

给定一个1-K的排列的康托展开形式,即S1*(K-1)! + S2*(K-2)! + S3*(K-3)!
+......+ SK(K-K)!;输出排列。

Limits

Time Limit(ms); 3000

Memory Limit(MB): No Limit

K: [1, 50000]

Si: [0, K - i]

Solution

根据康托展开的定义,可以看出,Si 表示的是当前比排列 i 位置的数小且没有在[1, i-1] 位置出现过的数的个数(具体可以参照百度百科或wiki)。问题则可以转化为:起初K个人排队,1,2,3,...,K;每次给定一个S,找到当前排第S位的人,让其出列,再如此循环,要求每次都准确找到排第S位的人是哪一个人。注意每一次S都可能不一样,出列后的人不再属于当前队伍。怎么做?用线段树动态查找就可以实现。

More

定义线段树每个结点表示不同的区间,根结点表示总区间,将区间二分成左孩子和右孩子,表示左半区间和右半区间,每个结点有一个域值,表示当前区间中含有的人数。起初,叶子结点域值为1,表示含有1个人,父亲域值等于左右孩子域值之和。Si +1实则表示当前是第S个人,用线段树动态查找,若左孩子域值大于等于S,则往左孩子查找,S不变;否则,往右孩子查找,S=S-左孩子域值。直到查到叶子结点为止,返回区间左端点(或右端点)。

用二分+线段树也可以实现,但复杂度为O(K*logK*logK)。

Complexity

Time Complexity: O(K*logK)

Memory Complexity: O(4*K)

Source

UVA 11525

百度百科关于康托展开的介绍

Code

UVA 11525 From My Github

时间: 2024-11-09 03:20:30

UVA 11525的相关文章

UVA 11525 Permutation(树状数组)

题目意思是说  给你一个数k  然后有k个si   问你1--k 的第n个全排列是多少   注意是 1 2 3...k的全排列 不是si的 N=   由观察得知(k-i)!就是k-i个数字的全排列种数, 0=<Si<=k-i,所以显然可知假设当i==1时从第(k-1)!*s1到第n个全排列都是由第S1+1个数字开始的数列,因为每(k-1)!次排列过后,下一个排列的第1个数字都要增大1(每隔(k-1)!次,这k-1个数字都排列过一遍了,下一次只能增大更前面一个,也就是第1个了) 比如对于数列{1

UVA - 11525 Permutation

题意:求1-k的排列中第n大的序列,题目给出n的计算方法: n = si*(k-1)+s2*(k-2)...+sk*0!; 并给你s1-sk 思路:首先我们明确,比如321是集合{1,2,3}的第几大的序列,从第一位开始3开头的话,那么显然这个序列的前面就一定会有1,2开头的学列,就是2*2!,依次类推我们就可以确定这个学列是第几大的了,但是要注意到用过的数将不再被我们考虑在内,现在这道题目是反过来了,可以琢磨一下si的含义是剩下没用的数中第(si+1)大的数,我们通过线段树来处理,0,1,分别

uva 11525 - Permutation(线段树)

题目链接:uva 11525 - Permutation 题目大意:给定n和k,n给定的方式为k个si,根据公式计算出n,求一个由1~k组成的长度为k的序列的第n个排序 解题思路:根据公式的性质,等于对于每个位置找当前状态下第si小的数.线段树子节点均为1,维护和,查询时传入参数查找即可. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int ma

Permutation UVA - 11525(值域树状数组,树状数组区间第k大(离线),log方,log)

Permutation UVA - 11525 看康托展开 题目给出的式子(n=s[1]*(k-1)!+s[2]*(k-2)!+...+s[k]*0!)非常像逆康托展开(将n个数的所有排列按字典序排序,并将所有排列编号(从0开始),给出排列的编号得到对应排列)用到的式子.可以想到用逆康托展开的方法.但是需要一些变化: for(i=n;i>=1;i--) { s[i-1]+=s[i]/(n-i+1); s[i]%=(n-i+1); } 例如:n=3时,3=0*2!+0*1!+3*0!应该变为3=1

uva 11525(单点修改)

题意:有一个由1到k组成的序列,最小是1 2 - k,最大是 k k-1 - 1,给出n的计算方式,n = s0 * (k - 1)! + s1 * (k - 2)! +- + sk-1 * 0!,给出s1-sk,输出序列里第n大的序列. 题解:通过找规律发现结果是可以递推的,比如第三组样例: 4 2 1 1 0 那么n = 2 * 3! + 1 * 2! + 1 * 1! + 0 * 0! = 15,通过把1~4的排列全写下来,发现3!是更换第一个数字的间隔,2!是更换第二个数字的间隔,以此类

uva 11525排列(树状数组 + 二分)

 现在给定k和n,要你按字典序输出 第n种排列的数列 而且题目给的 n是 n=S1(k-1)!+S2(k-2)!+...+Sk-1*1!+Sk*0!(0=<Si<=k-i), 我们可以知道si表示i后面有多少个比a[i]小的数,这样一来首先想到的就是set,但是set不能顺序访问,所以可以用树状数组,初始时置1,消除后置0,然后二分来求和为si + 1的位置 代码如下: #include<cstdio> #include<cstring> #include<c

UVA 11525 Permutation ——(线段树,脑筋急转弯)

只要注意到对于譬如:S1*(k-1)! 因为后面k-1个数字的全排列个数刚好是(k-1)!,那么第一个数字就是没有取过的数字的第(S1+1)个即可.取走这个数字以后这个数字就不能再用了,依次类推即可得到整个排列了. 那么随便用线段树维护一下即可. 代码如下: 1 #include <stdio.h> 2 #include <algorithm> 3 #include <string.h> 4 #define t_mid (l+r>>1) 5 #define

线段树&#183;二

1.UVA 11525 Permutation 题意:求1~k这k个数中第N个排列.(N从0开始记).N=sum(Si*(k-i)!)(1≤i≤k) 思路:根据N的值的性质,联系康拓展开,不妨发现第i位的值为剩下没用的数中从小到大第Si+1个.可以用线段树来记录区间内没有用的数的个数. 1 #include<iostream> 2 using namespace std; 3 int k; 4 const int maxk = 50010; 5 int numk[maxk]; 6 int tr

UVA 562 Dividing coins --01背包的变形

01背包的变形. 先算出硬币面值的总和,然后此题变成求背包容量为V=sum/2时,能装的最多的硬币,然后将剩余的面值和它相减取一个绝对值就是最小的差值. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 50007 int c[102],d