####
- 题目
P1338末日的传说 - 题意
构造一个字典序最小的逆序对数为 的序列 - 分析
是一个序列, 是它的逆序对数,则满足:
要让字典序最小,可以不动前面的序列,在后面的序列中重排使得逆序对数等于题目要求的 。需要的后面的序列的个数是 (满足不等式 ,最小的 ),那么就可以分成三部分去构造。- 第一部分 升序排列(对 没有贡献,使得字典序尽量小).
- 第二部分 前面的i形成的逆序对数是比 要大,要构成 ,需要把 中的第 个数放到这里.那么逆序对数就会减少到 .
- 第三部分 降序排列(对
所有的贡献).
构造样例:
根据上述的方法: - 先算出需要多少个数贡献逆序对数,算出是 ,所以 算出是 。序列是:
- 此时贡献的逆序对数是 ,与要求不符。找到原序列 第二个数放到第二部分中,就变成了 , 和 因为 的提前逆序对数均少了 ,总的就减少了 。取 放前面是因为要使得字典序最小,同时保证答案的正确。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main ()
{
ll n,m;
cin>>n>>m;
if(m == 0)
{
for(int i= 1;i<=n;i++)
cout<<i<<" ";
return 0;
}
for(ll i= 2;i <= n;i++)
{
if(i*(i-1)/2 >= m && (i-1)*(i-2)/2 < m)//找到第一个i的值同时后面部分(i-1)的逆序对不要超过m
{
int _ = i;//需要变成降序的个数,放到第三部分
int __ = i*(i-1)/2-m;//找到第_个值放到第二部分
for(int i =1;i<=n - _;i++)
cout<<i<<" ";//输出第一部分
cout<<n-__<<" ";//输出第二部分
for(int i= n;i>=(n-_+1);i--)
{
if(i != n-__)//注意不要把第二部分输出了
cout<<i<<" ";//降序输出第三部分
}
return 0;
}
}
return 0 ;
}