题面:
题解:
如何让逆序对数为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;
}