题目链接

题面:

题解:

如何让逆序对数为m的序列字典序最小呢? 假设位置p是最后一个原始序列的位置,那这个点应该尽量靠右,才能使得字典序最小。而为了保证有m个逆序对,要求p后面的逆序对数尽量大。需要降序排列。

这样问题就转成找点p,同时在找到p时还需要知道 m-后面所有逆序对数剩余的值,这个值要在点p身上修改。

则我们现在可以直接输出序列。 1)p之前的部分按照顺序输出 2)输出p,如果后面逆序对不够,则需要修改p点再输出。 3)逆序输出p之后的部分,注意如果p之前改点了,需要多判断一下。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<deque>
#define ll long long
#define llu unsigned ll
#define ld long double
#define iu unsigned int
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e16;
const double pi=acos(-1.0);
const int mod=1e9+7;
const int maxn=10100;
int main(void)
{
    int n,m;
    scanf("%d%d",&n,&m);
    //形成这些逆序对一共用了多少个数
    int cnt=0;

    //当前这一位最多能增加多少个逆序对
    int pm=1;

    //改动的第一位(转折点)需要增加多少个逆序对
    int minn=0;
    while(m)
    {
        minn=min(m,pm);
        m-=minn;
        pm++;
        cnt++;
    }
    //一共需要cnt+1位来形成这m个逆序对
    cnt=n-cnt;
    //令cnt=n-cnt
    //那么cnt-1位是不需要动的
    for(int i=1;i<cnt;i++)
        printf("%d ",i);
    //转折点为cnt+minn,那么他后面有cnt,cnt+1,。。。,cnt+minn-1共minn个数比它小
    //形成minn个逆序对
    printf("%d ",cnt+minn);

    //剩下的都是降序排列,形成尽量多的逆序对
    for(int i=n;i>=cnt;i--)
        if(i!=cnt+minn)
            printf("%d ",i);
    return 0;
}