【Educational Codeforces Round 102 C】No More Inversions
作者:互联网
题目链接
翻译
给你一个序列 \(a\), 是 1,2,3...k
按顺序组成的 \(n\ (n>=k)\) 个数字, 超过 \(k\) 了,又从右往左取。
然后,让你确定一个排列 \(p\),使得它按照 \(a\) 中元素作为下标顺序取,得到的序列 \(b\) 中逆序对的个
数不超过原序列 \(a\)。并且,要求得到的序列 \(b\) 的字典序是最大的。
题解
做这题之前,先得知道这么一个结论。
s[1],s[2],s[3]...s[p-2],s[p-1],s[p],s[p-1],s[p-2]...s[1]
这样的长度为 \(2*p\) 的序列,只要它满足任意两个数字都不同,那么不论 \(s\) 是啥,它的逆序对的个数都为 \((p-1)^2\)。
这样证:从中任取两个不相同的数字 \(x\) 和 \(y\),设他们在这个长度为 \(2*p\) 的序列中的相对位置如下:
x...y...y...x
或者是 x...y....x
。其中后者对应 \(y\) 是元素 s[p]
。
那么会发现,前者无论是 \(x>y\) 或者 \(x<y\)。对逆序对贡献都是 \(2\),而后者对逆序对贡献都是 \(1\)。
那么总的逆序对数就为 \(2*\frac{(p-1)*(p-2)}{2}+(p-1)\) 也即 \((p-1)^2\)。
怎么用这个结论呢。
我们设 \(m=n-k\)。
那么,\(a\) 根据下标就可以分为 \(1..k-m-1\) 和 \(k-m..k+m\) 这么两段。
而这里的第二段显然就是我们上面提到的逆序对数固定的部分,而第一部分单独不贡献逆序对,这两段之间因为第二段的
数字都大于第一段, 所以也不会贡献逆序对.
那么逆序对就全都在 \(k-m..k+m\) 这一段出现。
则我们新得到的长度为 \(n\) 的序列 \(b\) 也应该遵循这样的规则,即第一段不贡献逆序对 1,2,...k-m-1
。
然后第二段里面的值都比第一段大,但是第二段这时可以任意了,因为根据上面的证明,第二段里面数字是什么,最后
贡献的逆序对都是一样的。
当然选字典序最大的了,也即选 k,k-1,...k-m
。这样就组成了我们的排列 \(p\)。
代码
#include <bits/stdc++.h>
#define LL long long
using namespace std;
int T,n,k;
int main() {
#ifdef LOCAL_DEFINE
freopen("in.txt", "r", stdin);
#endif // LOCAL_DEFINE
cin >> T;
while (T--) {
cin >> n >> k;
int m = n - k;
for (int i = 1; i <= k - m - 1; i++) {
cout << i << " ";
}
for (int i = k - m,j=0; i <= k; i++,j++) {
cout << k - j << " ";
}
cout << endl;
}
return 0;
}
标签:...,Educational,..,No,int,Codeforces,第二段,序列,逆序 来源: https://www.cnblogs.com/AWCXV/p/14449192.html