preparing

「NOIP 模拟赛 20230705」T2 - 序列删数问题 题解

题面

原题

Natsuzora 有一个长度为 \(n\) 的排列 \(a_1,a_2,\ldots,a_n\),他想要将序列中的 \(m\) 个数删除。删除数字需要使用到“魔法工具”,其也有 \(m\) 种,其中第 \(i\) 种魔法工具能够将排列中任意一个的长度为 \(l_i\) 的区间中最大的数删除。每个魔法工具最多只能使用 \(1\) 次。每次删除操作后,序列的长度将减少 \(1\),且删去的数的右边所有数的下标减少 \(1\)。判断是否可以删去所有的 \(m\) 个数。\(1\le m\le n\le2\times 10^5\)

题解

不难证明,先用较大区间,给较大数使用较优。

考虑优化上述 \(\mathcal{O}(n^2)\) 的算法,一个数前后一定存在一段最大使用区间,例如:

\(10\ 5\ 3\ 6\ \color{red}{7\ 4}\ 9\ 2\ 8\ 1\) 中,要删去红色的数字。我们从大到小考虑每个要删的数字,对于 \(\color{red}7\) 来说,左右两边第一个不删的比其大的数分别是 \(\color{blue}{10}\)\(\color{blue} 9\),即其能接受的最大区间长度为 \(5\),如中括号括起来的一段:\(\color{blue}{10}\ [5\ 3\ 6\ \color{red}{7\ 4}]\ \color{blue}{9}\ 2\ 8\ 1\)。同理,\(\color{red}4\) 左右两边第一个不删的比其大的数分别是 \(\color{blue}6\)\(\color{blue} 9\),即其能接受的最大区间长度只有 \(1\)(注意 \(\color{red} 7\) 已经被删),如:\(10\ 5\ 3\ \color{blue}{6}\ \color{pink}{7}\ [\color{red}{4}]\ \color{blue}{9}\ 2\ 8\ 1\)

按照这种思路,我们可以得到正解:求出每个数能够接受的最大区间长度,比较是否可行即可。关于如何找到这个区间,左右两边第一个不删的比其大的数可以分别在两个单调栈上二分得到,在统计个数时用树状数组统计并删去比其大的要删去的数的个数即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 200005
using namespace std;
int T,n,m,a[maxn],cor[maxn],p[maxn],l[maxn],del[maxn],stk[maxn],top=0,le[maxn],ri[maxn],c[maxn],nd[maxn];
int lowbit(int x){return x&(-x);} void add(int p){for(;p<=n;p+=lowbit(p)) c[p]++;}
int query(int p){int res=0; for(;p>=1;p-=lowbit(p)) res+=c[p]; return res;}
void set(){for(int i=1;i<=n;i++) a[i]=cor[i]=p[i]=l[i]=del[i]=le[i]=ri[i]=c[i]=0; top=0;}
bool cmp(int aa,int bb){return a[aa]>a[bb];}
int main(){
//	freopen("deletion.in","r",stdin); freopen("deletion.out","w",stdout);
  	scanf("%d",&T); while(T--){
		set(); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){scanf("%d",&a[i]); cor[a[i]]=i;}
		for(int i=1;i<=m;i++){scanf("%d",&p[i]); del[p[i]]=1;} for(int i=1;i<=m;i++) scanf("%d",&l[i]);
		for(int i=1;i<=n;i++) if(del[i])
			{if(top==0||a[i]>stk[1]) le[i]=0; else le[i]=cor[stk[lower_bound(stk,stk+top+1,a[i],greater<int>())-stk-1]];}
			else{while(top>0&&stk[top]<a[i]) top--; stk[++top]=a[i];}
		top=0; for(int i=n;i>=1;i--) if(del[i])
			{if(top==0||a[i]>stk[1]) ri[i]=n+1; else ri[i]=cor[stk[lower_bound(stk,stk+top+1,a[i],greater<int>())-stk-1]];}
			else{while(top>0&&stk[top]<a[i]) top--; stk[++top]=a[i];}
		sort(p+1,p+1+m,cmp); for(int i=1;i<=m;i++){nd[i]=(ri[p[i]]-le[p[i]]-1)-(query(ri[p[i]]-1)-query(le[p[i]])); add(p[i]);}
		sort(nd+1,nd+1+m); for(int i=1;i<=m;i++){if(nd[i]<l[i]){printf("NO\n"); break;} if(i==m) printf("YES\n");}
	} return 0;
}
posted @ 2023-07-05 19:20  qzhwlzy  阅读(13)  评论(0编辑  收藏  举报