【2023NOIP模拟赛22】摘抄文档 做题记录

给定两个长 nn 的序列 aabb,每次操作可以选择一个满足 1i<n1\le i<nii 并把 aia_iai+1a_{i+1} 改为 max(ai,ai+1)\max(a_i,a_{i+1}),求任意次操作后 i=1n[ai=bi]\sum\limits_{i=1}^n[a_i=b_i] 的最大值。

1n1051\le n\le 10^51ai,bi1091\le a_i,b_i\le 10^9

设操作完后的 aa 序列为 aa'

显然操作相当于每个 aia_i 不断往外拓展,设 aia_i 拓展成了 a[li,ri]a'_{[l_i,r_i]}(没有任何 aja_j’aia_i 拓展来则把 aia_i 删掉),那么有个结论:

  • 一组 [li,ri][l_i,r_i] 合法当且仅当 i<j\forall i<j 都有 ri<ljr_i<l_j,即区间不交且相对顺序和 ii 的相对顺序相同。

那么不妨设 aia'_i 是由 pip_i 拓展而来的,不难发现 pip_i 单调递增。

发现钦定 ai=bia'_i=b_i 的所有 ii 都满足 pip_imaxji,aj=bi{j}\max\limits_{j\le i,a_j=b_i}\{j\}minji,aj=bi{j}\min\limits_{j\ge i,a_j=b_i}\{j\} 并不影响答案,那么不妨设这两个位置分别为 lbilb_irbirb_i(不存在则为 1-1)。

又发现 pip_i 可以为 lbilb_i 当且仅当 lbi=1lb_{i}\not=-1a[lbi,i]a_{[lb_i,i]} 中的最大值 bi\le b_i,可以为 rbirb_i 同理。那么不妨把所有不合法的 lblbrbrb 都赋值为 1-1

那么不难发现 ii 对答案有贡献当且仅当:

  • lbi=1lb_i\not= -1pi1lbip_{i-1}\le lb_i
  • rbi=1rb_i\not=-1pi1rbip_{i-1}\le rb_i

因为 lbi=1lb_i\not=-1rbi=1rb_i\not=-1 已经解决掉所有值域上的限制,只剩下 [li,ri][l_i,r_i] 相对顺序的限制。

那么考虑一个朴素的 dp:设 fi,jf_{i,j} 表示处理完 a[1,i]a'_{[1,i]}ai=aja'_i=a_jk=1i[ak=bk]\sum\limits_{k=1}^i[a'_k=b_k] 的最大值。显然有转移:

fi,j=fi1,j若 lbi=1,fi,lbi=maxjlbi{fi1,j}+1若 rbi=1,fi,rbi=maxjrbi{fi1,j}+1f_{i,j}=f_{i-1,j}\\ \text{若 }lb_i\not=-1,f_{i,lb_i}=\max\limits_{j\le lb_i}\{f_{i-1,j}\}+1\\ \text{若 }rb_i\not=-1,f_{i,rb_i}=\max\limits_{j\le rb_i}\{f_{i-1,j}\}+1

发现可以用树状数组维护前缀 max 解决,时间复杂度 O(nlogn)O(n\log n)

代码如下:

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

const int S=100005;

int n,a[S],b[S];
int m,tb[S*2];
int top,sta[S],pre[S],nxt[S];
int pos[S*2],lb[S],rb[S];
int c[S];

inline void add(int p,int val)
{
	for(int i=p;i<=n;i+=i&-i) c[i]=max(c[i],val);
}

inline int que(int p)
{
	int res=0;
	for(int i=p;i>=1;i-=i&-i) res=max(res,c[i]);
	return res;
}

int main()
{
	freopen("copy.in","r",stdin);
	freopen("copy.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) scanf("%d",&b[i]);
	for(int i=1;i<=n;i++) tb[++m]=a[i],tb[++m]=b[i];
	sort(tb+1,tb+m+1);
	m=unique(tb+1,tb+m+1)-tb-1;
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(tb+1,tb+m+1,a[i])-tb;
		b[i]=lower_bound(tb+1,tb+m+1,b[i])-tb;
	}
	sta[top=0]=0;
	for(int i=1;i<=n;i++)
	{
		while(top>0&&a[sta[top]]<a[i]) top--;
		pre[i]=sta[top];
		sta[++top]=i;
	}
	sta[top=0]=n+1;
	for(int i=n;i>=1;i--)
	{
		while(top>0&&a[sta[top]]<a[i]) top--;
		nxt[i]=sta[top];
		sta[++top]=i;
	}
	for(int i=1;i<=m;i++) pos[i]=-1;
	for(int i=1;i<=n;i++)
	{
		pos[a[i]]=i;
		lb[i]=pos[b[i]];
		if(lb[i]!=-1&&nxt[lb[i]]<=i) lb[i]=-1;
	}
	for(int i=1;i<=m;i++) pos[i]=-1;
	for(int i=n;i>=1;i--)
	{
		pos[a[i]]=i;
		rb[i]=pos[b[i]];
		if(rb[i]!=-1&&pre[rb[i]]>=i) rb[i]=-1;
	}
	// for(int i=1;i<=n;i++) printf("%d %d\n",lb[i],rb[i]);
	for(int i=1;i<=n;i++)
	{
		if(rb[i]!=-1) add(rb[i],que(rb[i])+1);
		if(lb[i]!=-1&&lb[i]!=rb[i]) add(lb[i],que(lb[i])+1);
	}
	printf("%d\n",que(n));
	return 0;
}