• 【佛山市选2013】排列——发现性质与转化问题


    排列

    一个关于n个元素的排列是指一个从{1, 2, …, n}到{1, 2, …, n}的一一映射的函数。这个排列p的秩是指最小的k,使得对于所有的i = 1, 2, …, n,都有p(p(…p(i)…)) = i(其中,p一共出现了k次)。
    例如,对于一个三个元素的排列p(1) = 3, p(2) = 2, p(3) = 1,它的秩是2,因为p(p(1)) = 1, p(p(2)) = 2, p(p(3)) = 3。
    给定一个n,我们希望从n!个排列中,找出一个拥有最大秩的排列。例如,对于n=5,它能达到最大秩为6,这个排列是p(1) = 4, p(2) = 5, p(3) = 2, p(4) = 1, p(5) = 3。
    当我们有多个排列能得到这个最大的秩的时候,我们希望你求出字典序最小的那个排列。对于n个元素的排列,排列p的字典序比排列r小的意思是:存在一个整数i,使得对于所有j < i,都有p(j) = r(j),同时p(i) < r(i)。对于5来说,秩最大而且字典序最小的排列为:p(1) = 2, p(2) = 1, p(3) = 4, p(4) = 5, p(5) = 3。

    输入的第一行是一个整数T(T <= 10),代表数据的个数。
    每个数据只有一行,为一个整数N。

    对于每个N,输出秩最大且字典序最小的那个排列。即输出p(1), p(2),…,p(n)的值,用空格分隔。

    输入:

    2

    5

    14

    输出:

    2 1 4 5 3

    2 3 1 5 6 7 4 9 10 11 12 13 14 8

    对于40%的数据,有1≤N≤100。
    对于所有的数据,有1≤N≤10000。

    分析:

    题目一眼看过去就知道是很多个环,而且再一眼看过去就知道是求环点数的最小公倍数最大时的方案。先不考虑字典序,对于这些环而言,他们的总点数要<=n,因为剩下的一个个单点可以形成子环,不影响最小公倍数。那么现在问题转化为一个有限背包问题:取几个互质的质数的c次方(有限)使得乘积最大,在dp的过程中我们可以同时记录对应的这几个数,少了再填几个1。现在再来考虑字典序,我们观察样例可以发现分成几个环后,它总是将首位移到这个环后面,例如1,2,3这三点构成的环,它们会形成2,3,1。再结合字典序最小,我们会期望将这几个环中,环点数少的放在前面,因为每次环中最小的会被放在最后,这样做能让小的数尽量往前,也就满足了字典序最小。

    当然这题还要注意的是,由于我们所处理的是几个数的乘积,在数据给的范围内是会超long long限制的,而在代码实现中我们也只需要比较这些最小公倍数的大小,我们可以找到一个单调递增并且增速很慢的函数来代替最小公倍数,我们会倾向于选择ln函数,也就是cmath里带的log函数。(这题最大的收获是这里,在只有比较大小的情况下,我们为了防爆long long,可以选择ln来优化)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<algorithm>
    using namespace std;
    #define debug printf("zjyvegetable
    ")
    #define int long long
    #define R register
    #define ld long double
    inline int read(){
        int a=0,b=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')b=-1;c=getchar();}
        while(isdigit(c)){a=a*10+c-'0';c=getchar();}
        return a*b;
    }
    const int N=1e5+50;
    int T,a[20],maxn,cnt,prime[N],ans[N],t[N][101],tot[N];
    bool bt[N];
    ld f[N];
    void find_pri(){
        for(R int i=2;i<=maxn;i++){
            if(!bt[i]){
                prime[++cnt]=i;
            }
            for(R int j=1;j<=cnt&&i*prime[j]<=maxn;j++){
                bt[i*prime[j]]=true;
            }
        }
    }
    signed main(){
        T=read();
        for(R int i=1;i<=T;i++){
            a[i]=read();
            maxn=max(maxn,a[i]);
        }
        find_pri();
        for(R int i=0;i<=maxn;i++)ans[i]=i;
        for(R int i=1;i<=cnt&&prime[i]<=maxn;i++){
            for(R int j=maxn;j>=prime[i];j--){
                for(int rem=prime[i];rem<=j;rem*=prime[i]){
    //                f[j]=max(f[j],f[j-rem]*rem);
                    if(f[j-rem]+log((ld)rem)>f[j]){
                        f[j]=f[j-rem]+log((ld)rem);
                        tot[j]=tot[j-rem];
                        for(R int p=1;p<=tot[j-rem];p++)
                        t[j][p]=t[j-rem][p];
                        t[j][++tot[j]]=rem;
                    }
                }
            }
        }
        for(R int i=2;i<=maxn;i++){
            if(f[ans[i]]<f[ans[i-1]])ans[i]=ans[i-1];
        }
        int sum,now;
        for(R int i=1;i<=T;i++){
            sum=0;now=0;
            sort(t[ans[a[i]]]+1,t[ans[a[i]]]+tot[ans[a[i]]]+1);
            for(R int j=1;j<=tot[ans[a[i]]];j++)
            sum+=t[ans[a[i]]][j];
            if(sum!=a[i]){
                for(;sum<a[i];sum++)
                printf("%lld ",++now);
            }
            for(R int j=1;j<=tot[ans[a[i]]];j++){
                for(R int k=1;k<t[ans[a[i]]][j];k++)
                printf("%lld ",now+k+1);
                printf("%lld ",now+1);
                now+=t[ans[a[i]]][j];
            }
            printf("
    ");
        }
        return 0;
    }
  • 相关阅读:
    【转载】Linux系统,设置Oracle开机启动,待整理
    【linux命令】grep
    Oracle 遇到的错误及处理整理
    【转载,整理】开启归档模式,归档日志已满处理
    【转载】【Oracle 11gR2】db_install.rsp详解
    CSS3属性选择器总结
    nginx负载均衡参数说明
    Nginx限制某个IP访问
    权限系统设计
    http-关于application/x-www-form-urlencoded等字符编码的解释说明
  • 原文地址:https://www.cnblogs.com/zjy1412/p/13456880.html
Copyright © 2020-2023  润新知