题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2520

题意:有一个排列1~k,求第n个排列,其中n为 uva 11525(线段树)K(1≤K≤50000),S1, S2 ,…, Sk.(0≤Si≤K-i).

分析:这道题目乍看之下没有什么好的思路,k!太大了,但是仔细看一看就会发现n和康托展开式很类似

如果不知道康托展开的话请看:http://www.doc88.com/p-293361248346.html

                                       http://blog.csdn.net/morgan_xww/article/details/6275460

要求第n个全排列,这不就是逆康托展开吗?

没错,仔细对比逆康托展开的推理过程就会发现,其实第n个全排列中的第i个数就是该排列中未出现过的比si大的第一个数。

比如:2 1 0   则比2大的第一个数是3,3未出现过,所以第一个数是3

                      比1大的第一个数是2,2未出现过,所以第二个数是2

                      比0大的第一个数是1,1未出现过,所以第三个数是1

        所以结果为3 2 1

 再比如:1 0 0   则比1大的第一个数是2,2未出现过,所以第一个数是2

                         比0 大的第一个数是1,1未出现过,所以第二个数是1

                         比0大的第一个数是1,但是1,2已经出现过了,所以第三个数是3

以此类推

普通的逆康托展开复杂度是O(n^2),这样对于k<=50000来说肯定是会超时的,可以用线段树(二分+树状数组)优化。

由上面的分析可知,

解法1:

线段树的具体做法同样是把 [1, K] 的数置成 1. 此时每条线段的权所代表的意义为在该区间内还有多少个数可以用。查找大于si的第一个未出现过的数,然后把这个数赋为0。 查找的时候如果左线段可用的数大于等于当前我们查找的数, 说明我们要找的数在左线段, 进入左线段, 查找的数不变; 否则说明在右线段, 进入右线段, 查找的数减去左线段可用的数的数目. 递归返回条件为当前节点代表的线段为单位线段(说明我们已经找到了)。

AC代码如下:

 1 #include<stdio.h>
 2 #define lson l,m,rt<<1
 3 #define rson m+1,r,rt<<1|1
 4 const int maxn=50000+10;
 5 int tree[maxn<<2],ans;
 6 void PushUp(int rt)
 7 {
 8     tree[rt]=tree[rt<<1]+tree[rt<<1|1];
 9 }
10 void build(int l,int r,int rt)
11 {
12     if(l==r)
13     {
14         tree[rt]=1;
15         return ;
16     }
17     int m=(l+r)>>1;
18     build(lson);
19     build(rson);
20     PushUp(rt);
21 }
22 void update(int p,int x,int l,int r,int rt)
23 {
24     if(l==r)
25     {
26         tree[rt]=x;
27         ans=l;
28         return ;
29     }
30     int m=(l+r)>>1;
31     if(p<=tree[rt<<1])
32         update(p,x,lson);
33     else
34         update(p-tree[rt<<1],x,rson);
35     PushUp(rt);
36 }
37 int main()
38 {
39     int t,n,i,x;
40     scanf("%d",&t);
41     while(t--)
42     {
43         scanf("%d",&n);
44         build(1,n,1);
45         for(i=0;i<n;i++)
46         {
47             scanf("%d",&x);
48             update(x+1,0,1,n,1);
49             printf("%d",ans);
50             if(i!=n-1)
51                 printf(" ");
52         }
53         printf("\n");
54     }
55     return 0;
56 }
View Code

相关文章:

  • 2021-07-31
  • 2021-10-13
  • 2022-12-23
  • 2022-12-23
  • 2022-01-20
  • 2022-12-23
  • 2021-09-30
  • 2022-02-26
猜你喜欢
  • 2021-09-16
  • 2021-12-05
  • 2021-12-04
  • 2022-12-23
  • 2021-12-26
  • 2022-12-23
  • 2021-07-07
相关资源
相似解决方案