Codeforces 1367 F2 Flying Sort

题意

给定一个由n个正整数构成的序列(序列中可能有相同元素),现在有两种操作:
1.选取序列中的任意一项,将其放置于序列首位;
2.选取序列中的任意一项,将其放置于序列末尾;
每个数最多操作一次.现需要将该序列变成不下降序列,请问至少需要操作几次。

分析

由于这些数只和大小关系有关,离散化一下,将值域变为\([1, n]\)
每个数如果要移动,最多只会移动一次。
那么,次数 = n - 不移动的数字个数。
我们考虑最大化不用移动的数字个数。
不用移动,意味着这些数在原数组中是有序的,而且值域上是连续的,而且每个数大于等于前一个数。

代码

//#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<sstream>
#include<string>
#include<cstring>
#include<cmath>
#include<map>
#include<iomanip>
#include<bitset>
#include<unordered_set>
#include<unordered_map>
#define IOS     cin.tie(0), ios::sync_with_stdio(false)
#define x first
#define y second
#define int long long

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const int N = 2e6 + 10, M = N * 2, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int P = 131;

int a[N], b[N];
int f[N];	// f[i] 表示以 a[i] 为不移动序列的最后一个数的最大序列长度
int cnt[N];	// 总次数 
int tot[N];	// 当前次数 
int fir[N]; // 第一次出现的位置 
int last[N];// 上一次出现的位置	
int n;

void solve() {
	cin >> n;
	for(int i = 1 ; i <= n ; i ++) cin >> a[i], b[i] = a[i];
	sort(b + 1, b + n + 1);
	int m = unique(b + 1, b + n + 1) - b - 1;	// 离散化 
	for(int i = 1 ; i <= n ; i ++) a[i] = lower_bound(b + 1, b + m + 1, a[i]) - b, cnt[a[i]] ++;
	
	int res = 0;
	for(int i = 1 ; i <= n ; i ++){
		int x = a[i];
		
		// x - 1 已经满了 	x 接到 x - 1 后面 
		if(tot[x - 1] == cnt[x - 1]) f[i] = max(f[i], f[fir[x - 1]] + cnt[x - 1]);
		
		// x 出现过  x 接到 x 上次出现后面 
		if(last[x]) f[i] = max(f[i], f[last[x]] + 1);
		
		// x - 1 还没有满  那么 x - 1 一定是作为不移动序列的最小的数
		if(last[x - 1]) f[i] = max(f[i], tot[x - 1] + 1);
		
		f[i] = max(f[i], 1ll);
		res = max(res, f[i]);
		last[x] = i;
		if(!fir[x]) fir[x] = i;
		tot[x] ++;
	} 
	// 总数 减去 不移动序列最大值 就是 答案 
	cout << n - res << "\n";
}



signed main() {
	IOS;
//	init();
	int T = 1;
//	cin >> T;
	while(T --) {
		solve();
	}
	return 0;
}
posted @ 2022-03-19 16:22  beatlesss  阅读(26)  评论(0编辑  收藏  举报