P1338末日的传说

####

末日的传说

  • 题目
    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} 0f(an)2(n1)n
    要让字典序最小,可以不动前面的序列,在后面的序列中重排使得逆序对数等于题目要求的 M M M。需要的后面的序列的个数是 i i i(满足不等式 ( i − 1 ) ∗ i 2 ≥ M \frac{(i-1)*i}{2}{\geq}M 2(i1)iM,最小的 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} m2(i1)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 2434,所以 i i i算出是 4 4 4。序列是: 1 ∣ 5432 1|5432 15432
    • 此时贡献的逆序对数是 6 6 6,与要求不符。找到原序列 i 中 的 2345 i中的2345 i2345第二个数放到第二部分中,就变成了 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 ;
}
点赞