*题解:P4375 [USACO18OPEN]Out of Sorts G

洗稿自 OU 的非常牛的题解,写出来纯粹是证明我看懂了。

原题链接

解析

首先我们来考虑单向冒泡的情况,此时我们向右进行交换,每轮会交换过来一个当前不在正确位置上的最大元素。反过来想,就是有一个不处于正确位置上的元素被交换往左走了一步。

所以,虽然每轮当中元素可能往右走很多步,但是正确位置在当前位置左边的元素必定往左走一步,也就是说,初始离正确位置最右的元素,离正确位置的距离即为所求。

对于双向冒泡,我们用类似的方法考虑,区别在于不处于正确位置上的元素可以往左走不止一步。从小到大考虑每个区间 \([i,n]\),每轮操作会将一个属于这个区间的元素移入正确位置,将一个不属于这个区间的元素移出区间。所以,初始时对于所有区间,不属于该区间的元素数量的最大值即为所求。

具体实现是,先将 \(a\) 离散化,然后按下标从大到小把 \(a_i\) 放到树状数组里,不属于区间 \([i,n]\) 的元素实际上就是排名比 \(i\) 小的元素。对于相同元素的处理,我们令初始下标更大的元素排名更大以避免原本属于区间的元素被归为不属于。

时间复杂度 \(O(n \log n)\)

代码

#include <bits/stdc++.h>
#define ls(p) ((p) << 1)
#define rs(p) (((p) << 1) | 1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e5 + 5,M = 6e6,mod = 998244353;
int a[N],b[N];
struct cmp{
	bool operator()(pii a,pii b){
		return a.first != b.first ? a.first < b.first : a.second < b.second;
	}
};
void add(int x,int k){
	for(;x < N;x += x & -x){
		b[x] += k;
	}
}
int ask(int x){
	int res = 0;
	for(;x;x -= x & -x){
		res += b[x];
	}
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	cin>>n;
	vector<pii> v;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		v.push_back({a[i],i});
	}
	sort(v.begin(),v.end());
	for(int i=1;i<=n;i++){
		int pos = lower_bound(v.begin(),v.end(),make_pair(a[i],i),cmp()) - v.begin() + 1;
		a[i] = pos;
	}
	int res = 1;
	for(int i=n;i>=1;i--){
		add(a[i],1);
		res = max(res,ask(i - 1));
	}
	cout<<res;
	return 0;
}
posted @ 2026-04-09 00:39  yutar  阅读(24)  评论(0)    收藏  举报