[Codeforces1223F]Stack Exterminable Arrays

本文介绍了一种算法,用于计算给定序列中可以被消除的子串数量。通过定义状态和转移方程,利用递归和哈希表优化查找过程,实现O(nlogn)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

给一个序列进行栈操作,从左到右入栈,若当前入栈元素等于栈顶元素则栈顶元素出栈,否则当前元素入栈。若进行完操作后栈为空,这说这个序列是可以被消除的。

给你一个长度为nnn的序列aaa,问aaa有多少子串是可以被消除的。


题解

定义fif_ifi表示序列ai,ai+1,...,ana_i,a_{i+1},...,a_nai,ai+1,...,an有多少可以被消除的子串

那么fi=fj+1+1f_i=f_{j+1}+1fi=fj+1+1jjj是使得ai,ai+1,...,aja_i,a_{i+1},...,a_jai,ai+1,...,aj能够被消除的最小的jjj

如果不存在此jjj,那么fi=0f_i=0fi=0

那么Ans=∑i=1nfiAns=\sum_{i=1}^nf_iAns=i=1nfi

考虑如何对于每一个iii求出这个jjj

NxiNx_iNxi表示此jjj,那么如果aNxi+1+1=aia_{Nx_{i+1}+1}=a_iaNxi+1+1=ai,则Nxi=Nxi+1+1Nx_i=Nx_{i+1}+1Nxi=Nxi+1+1

否则再判断aNxNxi+1+1+1=aia_{Nx_{Nx_{i+1}+1}+1}=a_iaNxNxi+1+1+1=ai,则Nxi=NxNxi+1+1+1Nx_i=Nx_{Nx_{i+1}+1}+1Nxi=NxNxi+1+1+1

如此递归直到找到一个NxkNx_kNxk或者Nxk+1>nNx_k+1>nNxk+1>n为止

这样显然耗时显然是巨大的,而我们每次又只要找aia_iai

考虑设Nyi,xNy_{i,x}Nyi,x表示使得ai,ai+1,...,aj−1a_i,a_{i+1},...,a_{j-1}ai,ai+1,...,aj1能够被消除的最小的jjjaj=xa_j=xaj=x

那么Nxi=Nyi+1,aiNx_i=Ny_{i+1,a_i}Nxi=Nyi+1,ai,如果ai−1≠aia_{i-1}\neq a_iai1=ai,那么ai−1a_{i-1}ai1NxNxNx就一定会跳到Nyi+1,ai+1Ny_{i+1,a_i}+1Nyi+1,ai+1处再进行判断

那么我们可以直接令Nyi=NyNxi+1Ny_i=Ny_{Nx_i+1}Nyi=NyNxi+1即可(这里C++C++C++mapmapmap使用swapswapswap可以做到O(1)O(1)O(1)

此时ai,ai+1,...,aNxia_i,a_{i+1},...,a_{Nx_i}ai,ai+1,...,aNxi是可以被消除的,所以Nyi,aNxi+1=Nxi+1Ny_{i,a_{Nx_i+1}}=Nx_i+1Nyi,aNxi+1=Nxi+1

然后也要把aia_iai加进去,即Nyi,ai=iNy_{i,a_i}=iNyi,ai=i

时间复杂度O(nlog⁡n)O(n\log n)O(nlogn)

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
	char c;T y=1;while(c=gc(),(c<48||57<c)&&c!=-1)if(c==45)y=-1;x=c-48;
	while(c=gc(),47<c&&c<58)x=x*10+c-48;x*=y;
}
const int N=3e5+5,M=N<<1,inf=~0u>>1;
typedef long long ll;
typedef int arr[N];
int n,a[N],Nx[N];ll f[N];
map<int,int>Ny[N];
inline void sol(){
	sd(n);
	fp(i,1,n){
		sd(a[i]);
		Ny[i].clear();
		Nx[i]=-1;f[i]=0;
	}
	Ny[n+1].clear();f[n+1]=0;
	int p;ll ans=0;
	fd(i,n,1){
		if(Ny[i+1].count(a[i])){
			Nx[i]=p=Ny[i+1][a[i]];
			swap(Ny[i],Ny[p+1]);
			if(p!=n)Ny[i][a[p+1]]=p+1;
		}
		Ny[i][a[i]]=i;
	}
	fd(i,n,1)if(~Nx[i]){
		f[i]=f[Nx[i]+1]+1;
		ans+=f[i];
	}
	printf("%lld\n",ans);
}
int main(){
	#ifndef ONLINE_JUDGE
		file("s");
	#endif
	int q;
	scanf("%d",&q);
	while(q--)sol();
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值