P1338 末日的传说

https://www.luogu.com.cn/problem/P1338
贪心,逆序对
黄色题
思路:

我们考虑把这个问题缩小范围。

比如n=5,在决定了最小的数“1”的位置之后,剩下的几个数是2 3 4 5,但是他们

具体是多少没必要关心,我们只要关心他们的相对大小关系。

所以考虑完当前最小的数,算出这个数对答案的贡献,然后减掉这个贡献,

就可以转而解决一个更小的子问题。(即n-->n-1)

回到题目上,要求是求一个有m个逆序对的字典序最小的排列。

我们知道一个长度为n的排列最多有(n-1)*n/2个逆序对,也知道一个排列的逆序对数越多,排列字典序越大。

所以如果当前m不比当前的(n-2)*(n-1)/2(也就是减少一个数之后的最多的逆序对数)大,

就可以直接把当前的最小数放在最前面,这肯定是最优的。

反之,则考虑最小数的放置位置。

假设当前排列长为n,最小数为a,则a有n种放法,放在从左到右第i个位置时会生成i-1个逆序对

(因为它左边有i-1个比他大)。

因为m大于n-1长度排列最多所能产生的逆序数,所以a不可能放在最前面,否则不满足条件。

怎么办呢?想到之前说的逆序对越多字典序越大,我们就必须让剩下的数能构成的逆序对数尽量小,所以a要放到最后,这样m减少的最多。

放完了a,问题就变成了n-1和m-(a的贡献)的子问题,递归求解即可。时间复杂度O(n)。


复制代码
typedef long long ll; ll n,m,a[50005]; int main(){ scanf("%lld%lld\n",&n,&m); ll lst=n,fst=1; for(int i=1;i<=n;i++){ ll t=(ll)(n-i)*(n-i-1)/2; if(t>=m)a[fst++]=i;//放头上 else a[lst--]=i,m-=(lst-fst+1);//放最后 } for(int i=1;i<=n;i++)printf("%d ",a[i]); return 0; }
复制代码

 


__EOF__

本文作者灰の魔女伊蕾娜
本文链接https://www.cnblogs.com/2elaina/p/16560488.html
关于博主:编程小萌新一名,希望从今天开始慢慢提高,一步步走向技术的高峰!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   -イレイナ  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示