CF1712A Wonderful Permutation 题解
传送门:洛谷,Codeforces
题意简述:给出一个 \(n\) 的排列,求最少要交换几次如 \(p_i,p_j\) 这样的数对,才能使得 \(\sum\limits_{i=1}\limits^{k}p_i\) 最小。
首先,我们思考一个事情:使得 \(\sum\limits_{i=1}\limits^{k}p_i\) 最小的方法是什么?
很简单就能想到,当然是将 \(p_i\) 中前 \(k\) 小的数全都放在前面,这样一来 \(\sum\limits_{i=1}\limits^{k}p_i\) 就是最小的了。
考虑到这是一个 \(n\) 的排列,所以前 \(k\) 小的数就是 \(1 , 2 , 3 \dots k\)。
好好想想不难得出一个结论,在交换 \(p_i,p_j\) 时,除了 \(p_i,p_j\) 以外的数都不会有影响。
而 \(\forall p_i|p_i \le k,i \le k\) 是不需要进行交换的,\(\forall p_i|p_i > k,i \le k\) 都需要进行交换,由于交换并不会影响到除了交换双方以外的数,所以 \(\forall p_i|p_i > k,i \le k\) 都只要与 \(p_i|p_i \le k,i > k\) 的数进行一次交换,之后就不用管它了。
所以,\(\forall p_i|p_i > k,i \le k\) 都会且仅会有一次关于它的交换,而且前 \(k\) 个数中小于 \(k\) 的数是不会被交换的,所以它们对答案没有贡献,而对于下标大于 \(k\) 的 \(p_i\),它们之间的顺序与答案无关。
由此我们便得到了一个 \(\mathcal{O}(t n)\) 的算法。
但为什么有些 \(\mathcal{O}(t n \log{n})\) 只要 \(0\) 毫秒,我 \(\mathcal{O}(t n)\) 却要 \(15\) 毫秒。
#include<iostream>
using namespace std;
const int N(105);
int t,n,k,p[N];
int main(){
#ifdef ytxy
freopen("in.txt","r",stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>t;
while(t--){
int sum=0;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>p[i];
if(i<=k&&p[i]>k) sum++;
}
cout<<sum<<'\n';
}
}

浙公网安备 33010602011771号