Gym103261C StalinSort Algorithm 优化DP

Problem – C – Codeforces

题意:你有一个斯大林排序算法,维护一个已排序序列,如果新加入的数仍然有序则不进行操作,否则可以毙掉新加的数或者序列里最后一个数,但是枪毙只能进行一次,如果毙掉原来的数后不满足有序则不能这样操作。

问对给定排列执行这个算法之后最少删除几个数,$n \le 5\times 10 ^ 5$。


不难看出加入一个新的数时只会和当前最后一个数有关系,所以可以维护一个DP数组$dp_i$表示以$i$结尾时最多保留几个数,那么如果$i<j$且$dp_i$可以转移到$dp_j$,则说明$i$和$j$可以共存于最后的序列中。

考虑如何维护这个东西。

题解的结论是这样的:设$next_i$表示第一个比$i$大的位置,则每个$i$可以转移到的$j$一定在$[next_i, next_{next_i})$内。(当然还要加上一个$a_i < a_j$的限制)

证明待补

有了这个结论之后逐个求DP值就行了,用一个线段树维护$a_i$这一维。

方便起见,可以规定$a_0 = 0$,$a_{n + 1} = n + 1$,这样就可以从一个必定在最终序列里的数开始DP,而后者是为了方便限制$next_i$。

#include <bits/stdc++.h>
 
using namespace std;
 
constexpr int maxn = 500005, maxm = (1 << 20) + 3;
 
int mx[maxm], M;
 
void modify(int x, int d) {
	mx[x += M + 1] = d;
 
	while (x > 1) {
		mx[x >> 1] = max(mx[x], mx[x ^ 1]);
		x >>= 1;
	}
}
 
int query(int r) {
	int ans = -0x3f3f3f3f, l = 1;
 
	for (l += M - 1, r += M + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
		if (~l & 1)
			ans = max(ans, mx[l ^ 1]);
		if (r & 1)
			ans = max(ans, mx[r ^ 1]);
	}
 
	return ans;
}
 
vector<pair<int, int> > u[maxn];
vector<int> v[maxn];
 
int dp[maxn], nx[maxn], a[maxn], stk[maxn];
 
int main() {
 
	int n;
	scanf("%d", &n);
 
	M = 1;
	while (M <= n + 1)
		M *= 2;
 
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	
	int top = 0;
	stk[0] = a[n + 1] = n + 1;
 
	for (int i = n; i; i--) {
		while (a[stk[top]] < a[i])
			top--;
		
		nx[i] = stk[top];
		
		stk[++top] = i;
	}
 
	memset(dp, -63, sizeof(dp));
	memset(mx, -63, sizeof(mx));
	
	dp[0] = 0;
 
	modify(0, 0);
	v[nx[1]].push_back(0);
 
	int ans = 0;
 
	for (int i = 1; i <= n; i++) {
		for (auto pi : u[i])
			modify(pi.first, pi.second);
		
		for (auto x : v[i])
			modify(x, -0x3f3f3f3f);
 
		int tmp = query(a[i]);
 
		if (tmp < 0)
			continue;
		
		dp[i] = tmp + 1;
		ans = max(ans, dp[i]);
 
		// printf("dp[%d] = %d\n", i, dp[i]);
		
		if (nx[i] == n + 1)
			continue;
		
		int l = nx[i], r = nx[nx[i]];
		// printf("l = %d  r = %d\n", l, r);
 
		u[l].push_back(make_pair(a[i], dp[i]));
		v[r].push_back(a[i]);
	}
 
	printf("%d\n", n - ans);
 
	return 0;
}

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注