####
末日的传说
- 题目
P1338末日的传说 - 题意
构造一个字典序最小的逆序对数为 M M M的序列 - 分析
a n a_n an是一个序列, f ( a n ) f(a_n) f(an)是它的逆序对数,则满足:
0 ≤ f ( a n ) ≤ ( n − 1 ) ∗ n 2 0{\leq}f(a_n){\leq}\frac{(n-1)*n}{2} 0≤f(an)≤2(n−1)∗n
要让字典序最小,可以不动前面的序列,在后面的序列中重排使得逆序对数等于题目要求的 M M M。需要的后面的序列的个数是 i i i(满足不等式 ( i − 1 ) ∗ i 2 ≥ M \frac{(i-1)*i}{2}{\geq}M 2(i−1)∗i≥M,最小的 i i i),那么就可以分成三部分去构造。- 第一部分 升序排列(对 M M M没有贡献,使得字典序尽量小).
- 第二部分 前面的i形成的逆序对数是比 M M M要大,要构成 M M M,需要把 i i i中的第 m − ( i − 1 ) ∗ i 2 m-\frac{(i-1)*i}{2} m−2(i−1)∗i个数放到这里.那么逆序对数就会减少到 M M M.
- 第三部分 降序排列(对 M M M所有的贡献).
构造样例:
5 5 5 4 4 4
12345 1 2 3 4 5 12345
根据上述的方法: - 先算出需要多少个数贡献逆序对数,算出是 4 ∗ 3 2 ≥ 4 \frac{4*3}{2}{\geq}4 24∗3≥4,所以 i i i算出是 4 4 4。序列是: 1 ∣ 5432 1|5432 1∣5432
- 此时贡献的逆序对数是 6 6 6,与要求不符。找到原序列 i 中 的 2345 i中的2345 i中的2345第二个数放到第二部分中,就变成了 13542 13542 13542, 5 5 5和 4 4 4因为 3 3 3的提前逆序对数均少了 1 1 1,总的就减少了 2 2 2。取 3 3 3放前面是因为要使得字典序最小,同时保证答案的正确。
#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 ;
}