1 条题解
-
0
自动搬运
来自洛谷,原作者为

zqy1018
**搬运于
2025-08-24 21:23:56,当前版本为作者最后更新于2017-06-17 22:54:29,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
我们考虑把这个问题缩小范围。
比如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; }
- 1
信息
- ID
- 336
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者