uoj#186 【UR #13】Yist

题目

orz myy

首先注意到答案有单调性,于是我们可以考虑二分一个(x),之后去判断一下每次只使用长度为(x)的区间能否删出目标序列

显然我们应该贪心地删除需要删除元素中最小的那一个,感性理解就是先删除最小的能使得接下来删除的限制尽量小

复杂度是(O(qn^2log n))

再大致理解一下发现我们并不需要二分,对于一个需要删除的元素,需要用到的最大区间长度是可以算出来的;我们搞一个单调栈,处理出每一个需要删除的元素左右两边第一个比它小的不需要删除的(l_i,r_i),再减去((l_i,r_i))这个开区间里需要删除的且比(a_i)小的元素就是可能的最大区间长度了(根据上面的贪心,这些元素之前就被删除了),答案即所有可能最大区间长度的最小值

由于我们不能将需要删除的元素加入单调栈,所以必须在单调栈上二分求出(l_i,r_i),复杂度是(O(qnlog n))

之后还要减掉((l_i,r_i))里需要删除且比(a_i)小的元素个数,看起来不是很好处理,但我们只需要减掉((l_i,r_i))里需要删除的元素个数即可,由于我们求得是最小值,这样并不会影响答案

考虑((l_i,r_i))里一个比(a_i)大的需要删除元素,这个元素形成的区间一定比(a_i)短,能形成的最小值一定比(a_i)形成的小

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=1e6+5;
int n,Q,top,cnt;
int st[maxn],a[maxn],l[maxn],r[maxn],pre[maxn];
char S[maxn];
inline int find(int x) {
	int L=1,R=top,nw=0;
	while(L<=R) {
		int mid=L+R>>1;
		if(a[st[mid]]<x) nw=st[mid],L=mid+1;else R=mid-1;
	}
	return nw;
}
int main() {
	n=read();a[0]=a[n+1]=-1;
	for(re int i=1;i<=n;i++) a[i]=read();
	Q=read();
	while(Q--) {
		scanf("%s",S+1);st[top=1]=0;cnt=0;
		for(re int i=1;i<=n;i++) 
		if(S[i]=='1') {
			while(top&&a[st[top]]>a[i]) --top;
			st[++top]=i;	
		}
		else l[i]=find(a[i])+1;
		st[top=1]=n+1;
		for(re int i=n;i;--i) 
		if(S[i]=='1') {
			while(top&&a[st[top]]>a[i]) --top;
			st[++top]=i;
		}
		else r[i]=find(a[i])-1,++cnt;
		for(re int i=1;i<=n;i++) pre[i]=pre[i-1]+(S[i]=='0');
		int ans=n;
		for(re int i=1;i<=n;i++) if(S[i]=='0') 
		ans=min(ans,r[i]-l[i]+1-pre[r[i]]+pre[l[i]-1]);
		printf("%d
",ans+1);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/11690143.html