【GDOI2003】排列的编码 {康托展开+高精度}

题目

Description
  对于n个元素的排列P=(p1,p2,……,pn),请你编写一个程序,在不构造出所有排列的情况下,直接输出该排列在按字典序排列的字典中的序数d(p),其中p1∈{1,2,3,…,n},1<=n<=50。例如:n=4,若p=(2,3,4,1),则d(p)=10;若p=(4,2,1,3),则d(p)=21

Input
  每一行对应一个数据,格式为(n,(p1,p2,….,pn))其中n表示排列的元素个数,(p1,p2,…pn)就是这n个元素的某个排列。文件的最后一行只包含“-1”,表示输入文件结束。

Output
  对于每个数据,输出对应的d(p)。所有数据的结果都输出到一行中,用逗号分开。


解题思路

虽然今天的题目都很简单,有两位同学改做B组题。但我是真的找不到规律,于是什么都没有敲。正解是康托展开:

X = a [ n ] ( n 1 ) ! + a [ n 1 ] ( n 2 ) ! + . . . + a [ 2 ] 1 ! + a [ 1 ] 0 ! [ a [ i ] 0 0 <= a [ i ] < i ( 1 <= i <= n ) ]

因为数据较大,还要外加两个高精乘单精


代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
using namespace std; 
int n,a[81],b[55][150],ans[150]; bool l; 
void dff(int k)
{
    for (int i=1;i<=145;i++)
    {
     b[k][i]=b[k][i]+b[k-1][i]*k; 
     b[k][i+1]=b[k][i+1]+b[k][i]/10; 
     b[k][i]=b[k][i]%10; 
    }
}
void df(int z,int k)
{
    for (int i=1;i<=145;i++)
    {
     ans[i]=ans[i]+b[k][i]*z; 
     ans[i+1]=ans[i+1]+ans[i]/10; 
     ans[i]=ans[i]%10; 
    }
}
int main()
{

    b[0][1]=1; string s; 
    for (int i=1;i<=50;i++) dff(i); 
    while (1)
    {
        cin>>s; int e=0,t=-1; bool bb=0; 
        for (int i=0;i<s.size();i++)
        {
          if (s[i]=='-') return 0; 
          if (s[i]>='0'&&s[i]<='9') { bb=1; e=e*10+s[i]-'0'; }
           else if (bb==1) { a[++t]=e; e=0; bb=0;}
        }
        n=a[0]; a[0]=0; 
        memset(ans,0,sizeof(ans)); 
        ans[1]=1; 
        if (l==1) printf(","); 
        if (n!=-1) l=1;
        for (int i=1;i<n;i++)   
         {
            int g=0; 
            for (int j=1;j<i;j++)
             if (a[j]<a[i]) g++; 
             df(a[i]-1-g,n-i);
         }
        int q=145; 
        while (q>1&&ans[q]==0) q--; 
        for (int i=q;i>=1;i--)
         printf("%d",ans[i]); 
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39897867/article/details/80972515
今日推荐