zoukankan      html  css  js  c++  java
  • Permutation UVA

    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*2!+1*1!+0*0!。就是“能放到前面的尽量放到前面”。

    然后,生成这个排列的方法就是:首先有一个集合,把1~n的所有数放进集合。然后从小到大枚举答案的位置i,对于每个i,取出当前集合中第(s[i]+1)大的元素输出,并从集合中去掉这个元素。

    这么做的原因是:对于某个位置i,取当前集合中第x大的元素,那么就“跳过”了(x-1)!个元素。

    因此可以用任意一种平衡树水过去

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<ctime>
      4 #include<algorithm>
      5 using namespace std;
      6 #define MAXI 2147483647
      7 //http://blog.csdn.net/h348592532/article/details/52837228随机数
      8 int rand1()
      9 {
     10     static int x=471;
     11     return x=(48271LL*x+1)%2147483647;
     12 }
     13 struct Node
     14 {
     15     Node* ch[2];
     16     int r;//优先级
     17     int v;//value
     18     int size;//维护子树的节点个数
     19     int num;//当前数字出现次数
     20     int cmp(int x) const//要在当前节点的哪个子树去查找,0左1右
     21     {
     22         if(x==v)    return -1;
     23         return v<x;//x<v?0:1
     24     }
     25     void upd()
     26     {
     27         size=num;
     28         if(ch[0]!=NULL)    size+=ch[0]->size;
     29         if(ch[1]!=NULL)    size+=ch[1]->size;
     30     }
     31 }nodes[200100];
     32 int mem,n;
     33 Node* root=NULL;
     34 void rotate(Node* &o,int d)
     35 {
     36     Node* t=o->ch[d^1];o->ch[d^1]=t->ch[d];t->ch[d]=o;
     37     o->upd();t->upd();//o是t子节点,一定要这个顺序upd
     38     o=t;//将当前节点变成旋转完后新的父节点
     39 }
     40 Node* getnode()
     41 {
     42     return &nodes[mem++];
     43 }
     44 void insert(Node* &o,int x)
     45 {
     46     if(o==NULL)
     47     {
     48         o=getnode();o->ch[0]=o->ch[1]=NULL;
     49         o->v=x;o->r=rand1();o->num=1;
     50     }
     51     else
     52     {
     53         if(o->v==x)    ++(o->num);
     54         else
     55         {
     56             int d=o->v < x;//x < o->v?0:1
     57             insert(o->ch[d],x);
     58             if(o->r < o->ch[d]->r)    rotate(o,d^1);//不是 x < o->ch[d]->r
     59         }
     60     }
     61     o->upd();
     62 }
     63 void remove(Node* &o,int x)
     64 {
     65     int d=o->cmp(x);
     66     if(d==-1)
     67     {
     68         if(o->num > 0)
     69         {
     70             --(o->num);
     71         }
     72         if(o->num == 0)
     73         {
     74             if(o->ch[0]==NULL)    o=o->ch[1];
     75             else if(o->ch[1]==NULL)    o=o->ch[0];
     76             else
     77             {
     78                 //int d2= o->ch[0]->r > o->ch[1]->r;//o->ch[0]->r > o->ch[1]->r ? 1:0
     79                 int d2=o->ch[1]->r < o->ch[0]->r;//o->ch[1]->r <= o->ch[0]->r
     80                 rotate(o,d2);
     81                 remove(o->ch[d2],x);
     82                 //左旋则原节点变为新节点的左子节点,右旋相反
     83             }
     84         }
     85     }
     86     else    remove(o->ch[d],x);
     87     if(o!=NULL)    o->upd();
     88 }
     89 bool find(Node* o,int x)
     90 {
     91     int d;
     92     while(o!=NULL)
     93     {
     94         d=o->cmp(x);
     95         if(d==-1)    return 1;
     96         else o=o->ch[d];
     97     }
     98     return 0;
     99 }
    100 int kth(Node* o,int k)
    101 {
    102     if(o==NULL||k<=0||k > o->size)    return 0;
    103     int s= o->ch[0]==NULL ? 0 : o->ch[0]->size;
    104     if(k>s&&k<=s+ o->num)    return o->v;
    105     else if(k<=s)    return kth(o->ch[0],k);
    106     else    return kth(o->ch[1],k-s- o->num);
    107 }
    108 int rk(Node* o,int x)
    109 {
    110     int r=o->ch[0]==NULL ? 0 : o->ch[0]->size;
    111     if(x==o->v)    return r+1;
    112     else    if(x<o->v)    return rk(o->ch[0],x);
    113     else    return r+ o->num +rk(o->ch[1],x);
    114 }
    115 int pre(Node* o,int x)
    116 {
    117     if(o==NULL)    return -MAXI;
    118     int d=o->cmp(x);
    119     if(d<=0)    return pre(o->ch[0],x);
    120     else    return max(o->v,pre(o->ch[1],x));
    121 }
    122 int nxt(Node* o,int x)
    123 {
    124     if(o==NULL)    return MAXI;
    125     int d=o->cmp(x);
    126     if(d!=0)    return nxt(o->ch[1],x);
    127     else    return min(o->v,nxt(o->ch[0],x));
    128 }
    129 int xx[50100];
    130 int T;
    131 int main()
    132 {
    133     int i;
    134     scanf("%d",&T);
    135     while(T--)
    136     {
    137         root=NULL;mem=0;
    138         scanf("%d",&n);
    139         for(i=1;i<=n;i++)    insert(root,i);
    140         for(i=1;i<=n;i++)    scanf("%d",&xx[i]);
    141         for(i=n;i>=1;i--)
    142         {
    143             xx[i-1]+=xx[i]/(n-i+1);
    144             xx[i]%=(n-i+1);
    145         }
    146         for(i=1;i<n;i++)
    147         {
    148             printf("%d ",kth(root,xx[i]+1));
    149             remove(root,kth(root,xx[i]+1));
    150         }
    151         printf("%d
    ",kth(root,xx[n]+1));//这题卡格式
    152         remove(root,kth(root,xx[n]+1));
    153     }
    154     return 0;
    155 }

    同样可以用树状数组做。树状数组中存某个值出现的次数。也就是说,开始的集合中,如果数字x出现了y次,就在树状数组的位置x处加y。

    第k大数,就是有至少k个数小于等于它的最小数。

    那么,如果要求第k大数,就二分第k大数的值p,显然可以在log的时间内求出小于等于p的数的个数q,就是树状数组位置p的前缀和。如果p大于等于k,那么显然第k大数在1~p之间,否则第k大数在p+1~n之间。

    这个二分貌似很难用左闭右开区间写出来

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #define lowbit(x) ((x)&(-x))
     5 using namespace std;
     6 int dat[50100],n;
     7 int sum(int k)//前k数的前缀和
     8 {
     9     int ans=0;
    10     while(k>0)
    11     {
    12         ans+=dat[k];
    13         k-=lowbit(k);
    14     }
    15     return ans;
    16 }
    17 void add(int pos,int x)
    18 {
    19     while(pos<=n)
    20     {
    21         dat[pos]+=x;
    22         pos+=lowbit(pos);
    23     }
    24 }
    25 int kth(int k)
    26 {
    27     int l=1,r=n,m;
    28     while(r>l)
    29     {
    30         m=l+((r-l)>>1);
    31         if(sum(m)>=k)
    32             r=m;
    33         else
    34             l=m+1;
    35     }
    36     return l;
    37 }
    38 int xx[50100];
    39 int T;
    40 int main()
    41 {
    42     int i,t;
    43     scanf("%d",&T);
    44     while(T--)
    45     {
    46         memset(dat,0,sizeof(dat));
    47         scanf("%d",&n);
    48         for(i=1;i<=n;i++)    add(i,1);
    49         for(i=1;i<=n;i++)    scanf("%d",&xx[i]);
    50         for(i=n;i>=1;i--)
    51         {
    52             xx[i-1]+=xx[i]/(n-i+1);
    53             xx[i]%=(n-i+1);
    54         }
    55         for(i=1;i<n;i++)
    56         {
    57             t=kth(xx[i]+1);
    58             printf("%d ",t);
    59             add(t,-1);
    60         }
    61         printf("%d
    ",kth(xx[n]+1));
    62     }
    63     return 0;
    64 }

    还有一个log的写法

    例如现在有一个数列1 2 3 3 4 5 7 8 9 9
    值域数组a为1 1 2 1 1 0 1 1 2
    c(树状数组直接存的值)为1 2 2 5 1 1 1 8 2
    先找到小于第k大的数的最大数,也就是sum(x)<k的最大的x
    (找第7大,k=7,答案x=5(101(2)))
    一开始x=0,cnt(记录这个数之前已经累加的sum)=0
    那么从第4位开始判,x+2^4>=n,所以啥也不干
    x+2^3<n,cnt+c[x+2^3]=8 >= 7 所以啥也不干
    x+2^2<n,cnt+c[x+2^2]=5 <7 所以 cnt+=c[x+2^2],x+=2^2 cnt=5,x=4
    x+2^1<n, cnt+c[x+2^1]=7 >=7 所以啥也不干
    x+2^0<n, cnt+c[x+2^0]=6 < 7 所以 cnt+=c[x+2^0],x+=2^0 cnt=6,x=5
    原因:
    记当前处理的位为i(也就是x+2^i,c[x+2^i]),那么每一次处理前x显然满足转换为二进制后从低位开始数前i+1位没有1(从高位开始处理,每次只加2^x,因此高位只可能在i+1位之后产生过1)
    那么,根据树状数组的定义,c[x+2^i]就是a[x+1]加到a[x+2^i]的和
    再参考一下这个:
    求第K小的值。a[i]表示值为i的个数,c[i]当然就是管辖区域内a[i]的和了。
    神奇的方法。不断逼近。每次判断是否包括(ans,ans + 1 << i]的区域,
    不是的话减掉,是的话当前的值加上该区域有的元素。

    http://blog.csdn.net/z309241990/article/details/9623885

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #define lowbit(x) ((x)&(-x))
     5 using namespace std;
     6 int dat[50100],n,n2;
     7 //n2为值域,此处与n相同
     8 void add(int pos,int x)
     9 {
    10     while(pos<=n2)
    11     {
    12         dat[pos]+=x;
    13         pos+=lowbit(pos);
    14     }
    15 }
    16 int kth(int k)
    17 {
    18     int x=0,cnt=0,i;
    19     for(i=16;i>=0;i--)
    20     {
    21         x+=(1<<i);
    22         if(x>=n2||cnt+dat[x]>=k)    x-=(1<<i);
    23         else    cnt+=dat[x];
    24     }
    25     return x+1;
    26 }
    27 int xx[50100];
    28 int T;
    29 int main()
    30 {
    31     int i,t;
    32     scanf("%d",&T);
    33     while(T--)
    34     {
    35         memset(dat,0,sizeof(dat));
    36         scanf("%d",&n);n2=n;
    37         for(i=1;i<=n;i++)    add(i,1);
    38         for(i=1;i<=n;i++)    scanf("%d",&xx[i]);
    39         for(i=n;i>=1;i--)
    40         {
    41             xx[i-1]+=xx[i]/(n-i+1);
    42             xx[i]%=(n-i+1);
    43         }
    44         for(i=1;i<n;i++)
    45         {
    46             t=kth(xx[i]+1);
    47             printf("%d ",t);
    48             add(t,-1);
    49         }
    50         printf("%d
    ",kth(xx[n]+1));
    51     }
    52     return 0;
    53 }

    还有值域线段树做法,建树与树状数组类似。查找k大也是一个log,方法如下:

    求区间第K大可以用线段树,以值为区间建树,区间和表示这段区间的数出现了多少次,然后就从根节点开始,
    根据左子树与k的大小比较选择向左向右查找,最终到达的叶子就是我们要找的第k大的值

    参考:http://blog.csdn.net/qq_38678604/article/details/78575672

    曾经错误:线段树清空出错,少了20行,WA

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define lc (num<<1)
     4 #define rc (num<<1|1)
     5 #define mid ((l+r)>>1)
     6 typedef int LL;
     7 
     8 LL tree[400100],laz[400100];
     9 LL L,R,x,n,m;
    10 void build(LL l,LL r,LL num)
    11 {
    12     if(l==r)
    13     {
    14         tree[num]=1;
    15         return;
    16     }
    17     build(l,mid,lc);
    18     build(mid+1,r,rc);
    19     tree[num]=tree[lc]+tree[rc];
    20     laz[num]=0;
    21 }
    22 void pushdown(LL l,LL r,LL num)
    23 {
    24     if(laz[num])
    25     {
    26         laz[lc]+=laz[num];
    27         laz[rc]+=laz[num];
    28         tree[lc]+=(mid-l+1)*laz[num];
    29         tree[rc]+=(r-mid)*laz[num];
    30         laz[num]=0;
    31     }
    32 }
    33 void update(LL l,LL r,LL num)
    34 {
    35     if(L<=l&&r<=R)
    36     {
    37         tree[num]+=(r-l+1)*x;
    38         laz[num]+=x;
    39         return;
    40     }
    41     pushdown(l,r,num);
    42     if(L<=mid)    update(l,mid,lc);
    43     if(mid<R)    update(mid+1,r,rc);//if(mid+1<=R)
    44     /*important*/tree[num]=tree[lc]+tree[rc];
    45 }
    46 LL query(LL l,LL r,LL num)
    47 {
    48     if(L<=l&&r<=R)    return tree[num];
    49     pushdown(l,r,num);
    50     LL ans=0;
    51     if(L<=mid)    ans+=query(l,mid,lc);
    52     if(mid<R)    ans+=query(mid+1,r,rc);
    53     return ans;
    54 }
    55 LL kth(LL l,LL r,LL num,LL k)
    56 {
    57     if(l==r)    return l;
    58     pushdown(l,mid,lc);
    59     pushdown(mid+1,r,rc);
    60     if(tree[lc]>=k)    return kth(l,mid,lc,k);
    61     else    return kth(mid+1,r,rc,k-tree[lc]);
    62 }
    63 LL xx[50100];
    64 int T;
    65 int main()
    66 {
    67     LL i,t;
    68     scanf("%d",&T);
    69     while(T--)
    70     {
    71         scanf("%d",&n);
    72         build(1,n,1);
    73         for(i=1;i<=n;i++)    scanf("%d",&xx[i]);
    74         for(i=n;i>=1;i--)
    75         {
    76             xx[i-1]+=xx[i]/(n-i+1);
    77             xx[i]%=(n-i+1);
    78         }
    79         for(i=1;i<n;i++)
    80         {
    81             pushdown(1,n,1);
    82             t=kth(1,n,1,xx[i]+1);
    83             printf("%d ",t);
    84             L=R=t;x=-1;
    85             update(1,n,1);
    86         }
    87         pushdown(1,n,1);
    88         printf("%d
    ",kth(1,n,1,xx[n]+1));
    89     }
    90     return 0;
    91 }
  • 相关阅读:
    在sql语句中使用 xml for path 格式化字符串的方法总结
    Android handler的使用简单示例
    easyui datagrid中 多表头方法总结
    使用ICSharpCode.SharpZipLib.Zip类库解压zip文件的方法
    ThreadPoolExecutor 优雅关闭线程池的原理.md
    ThreadPoolExecutor 几个疑惑与解答
    如何在运行时(Runtime)获得泛型的真正类型
    为什么 EXISTS(NOT EXIST) 与 JOIN(LEFT JOIN) 的性能会比 IN(NOT IN) 好
    Spring MVC 上下文(ApplicationContext)初始化入口
    Tomcat生成的session持久化到MySQL
  • 原文地址:https://www.cnblogs.com/hehe54321/p/7895779.html
Copyright © 2011-2022 走看看