【洛谷1338】末日的传说
原题:
咱这规律找滴彳亍不彳亍
这个表是通过暴力枚举打的表,最左一列数字是逆序对个数,右边的是对应的字典序最小的排列,即所求答案
现在可以来研究一下为什么会产生这种规律
首先考虑一个特殊情况,当逆序对个数为m*(m-1)/2时,一个长度为m的递减序列就可以完成
在递增序列的基础上,若使字典序最小,肯定尽可能只变动后面的数字,当逆序对个数为m*(m-1)/2时,只将后m个数字倒序排列是最优的
因此就出现了图中粉色部分和黄色部分,当逆序对个数位于(m-1)*(m-2)/2和m*(m-1)/2之间时,只对后m个数字重排列
那么如果逆序对个数为m*(m-1)/2+1,此时就不能只变动后m个数字,从右往左第m+1个数字必须发生变化
若使字典序最小,因为前n-m-1个数字不变,则第n-m个(即从右往左第m+1个)数字应尽量小,而它又必须比n-m大(原来是n-m)
因此此数字至少应该是n-m+1
后m个数字中,比n-m+1小的只有n-m一个,因此n-m+1只能提供一组逆序对,还需要m*(m-1)/2个逆序对
因此剩下的m个数字必须呈降序排列
如果逆序对个数继续增加到m*(m-1)/2+2,则第n-m个数字为n-m+1也无法满足需求,因为后m个数字是降序的,已经达到最大逆序对
因此第n-m个数字必须再次增加,变为n-m+2
此时后m个数字中比n-m+2小的有n-m和n-m+1两个,还需要m*(m-1)/2个逆序对,于是后m个数字又必须降序排列……
依此类推,每当逆序对个数+1,第n-m个数字就+1,这就形成了图中的红色部分
剩余的数字降序排列,形成图中黄色和蓝色部分
其中蓝色部分比红色部分大,黄色部分比红色部分小
研究过规律的原理后,便可以找到一种正推出做法的思路(而不是靠打表观察或灵稽一动)
当逆序对个数为0时,序列为递增序列,若逆序对个数+1,则交换最后两个数
接下来再让逆序对+1,变为2,从左至右考虑每一位数字是否有必要改变
结果发现倒数第3个数字不得不发生变化,因为后2个数字为降序排列
于是让倒数第3个数字+1,变成n-1,然后发现n-1只能和n-2组成一组逆序对,还有2-1=1个逆序对需要解决
而1=2*(2-1)/2,因此后2个数必须为降序,由此得到逆序对个数为2的解
再让逆序对+1,因为后2个数必须为降序,所以倒数第3个数不得不变化
让倒数第3个数字+1,变成n-2……
类推上述步骤即可发现规律与构造方法
代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int n,m; 5 int main(){ 6 cin>>n>>m; 7 int b=0; 8 for(b=0;b<n && m>b;++b) m-=b; 9 for(int i=1;i<=n-b-1;++i) printf("%d ",i); 10 printf("%d ",n-(b-m)); 11 for(int i=1,j=n;i<=b-m;++i,--j) printf("%d ",j); 12 for(int i=1,j=n-(b-m)-1;i<=m;++i,--j) printf("%d ",j); 13 cout<<endl; 14 return 0; 15 }