• 洛谷 P1338 末日的传说 (字典序 + 逆序对)


    这道题需要对排列有深刻的理解和认识
    给出逆序对的个数,求改逆序对个数的字典序最小的排列
    那么既然是最小,那么一开始一段肯定是升序,一直到某个数后才开始改变
    即1 2 3…… n-1 n a b c d……
    类似这样
    那么我们要求出这个n在哪里
    要字典序最小,就需要1到n这一段最长
    也就是说在a, b, c, d后面有尽量多的逆序对
    当数列为n n-1 n-2 ……1时逆序对最多
    逆序对个数就是n - 1 + n - 2 ……+1 
    那么我们就可以从小到大枚举a b c……的长度,算出最多逆序对的个数
    找到一个临界值,即第一次逆序对个数第一次大于m的时候
    这个时候就可以根据长度求出n了
    所以1到n-1直接输出
    为什么不输出n呢
    因为不一定后面的逆序对刚好为m
    所以需要对n这个位置做调整,使得逆序对为m
    设从n + 1开始往后的长度逆序对个数为len (注意不包括n)
    那么假设 m - len=x
    那么n的位置就输出n + x
    因为后面的序列是n + 1,n + 3……
    比n + x小的有n + 1, n + 2……n + x - 1 刚好x个数
    就把差值补上来了。
    然后后面就逆序输出就行了。
    具体看代码
     

    #include<cstdio>
    #include<algorithm>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    using namespace std;
    
    const int MAXN = 51234;
    int a[MAXN], n, m;
    
    int main()
    {
        scanf("%d%d", &n, &m);
        int p = n, c;
        for(int s = 1; m; p--, s++)
        {
            if(m > s) m -= s;
            else { c = m; m = 0; }
        }
        
        REP(i, 1, p) printf("%d ", i);
        printf("%d ", p + c);
        for(int i = n; i >= p; i--)
            if(i != p + c)
                printf("%d ", i);
        puts("");
        
        return 0;
    }
  • 相关阅读:
    Oracle 创建索引分区
    Shiro SessionManager会话管理器设计概念
    管理表分区
    Oracle 表分区策略
    Oracle 表分区
    参考Shiro的Session定期验证会话失效的线程池
    HDU 3555 Bomb (数位dp)
    POJ 3666 Making the Grade (动态规划)
    Gym
    POJ
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819346.html
Copyright © 2020-2023  润新知