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';
	}
}
posted @ 2022-08-14 11:32  JR_ytxy  阅读(25)  评论(0)    收藏  举报