[Codeforces1223F]Stack Exterminable Arrays

题意

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

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


题解

定义 f i f_i fi表示序列 a i , a i + 1 , . . . , a n a_i,a_{i+1},...,a_n ai,ai+1,...,an有多少可以被消除的子串

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

如果不存在此 j j j,那么 f i = 0 f_i=0 fi=0

那么 A n s = ∑ i = 1 n f i Ans=\sum_{i=1}^nf_i Ans=i=1nfi

考虑如何对于每一个 i i i求出这个 j j j

N x i Nx_i Nxi表示此 j j j,那么如果 a N x i + 1 + 1 = a i a_{Nx_{i+1}+1}=a_i aNxi+1+1=ai,则 N x i = N x i + 1 + 1 Nx_i=Nx_{i+1}+1 Nxi=Nxi+1+1

否则再判断 a N x N x i + 1 + 1 + 1 = a i a_{Nx_{Nx_{i+1}+1}+1}=a_i aNxNxi+1+1+1=ai,则 N x i = N x N x i + 1 + 1 + 1 Nx_i=Nx_{Nx_{i+1}+1}+1 Nxi=NxNxi+1+1+1

如此递归直到找到一个 N x k Nx_k Nxk或者 N x k + 1 > n Nx_k+1>n Nxk+1>n为止

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

考虑设 N y i , x Ny_{i,x} Nyi,x表示使得 a i , a i + 1 , . . . , a j − 1 a_i,a_{i+1},...,a_{j-1} ai,ai+1,...,aj1能够被消除的最小的 j j j a j = x a_j=x aj=x

那么 N x i = N y i + 1 , a i Nx_i=Ny_{i+1,a_i} Nxi=Nyi+1,ai,如果 a i − 1 ≠ a i a_{i-1}\neq a_i ai1=ai,那么 a i − 1 a_{i-1} ai1 N x Nx Nx就一定会跳到 N y i + 1 , a i + 1 Ny_{i+1,a_i}+1 Nyi+1,ai+1处再进行判断

那么我们可以直接令 N y i = N y N x i + 1 Ny_i=Ny_{Nx_i+1} Nyi=NyNxi+1即可(这里 C + + C++ C++ m a p map map使用 s w a p swap swap可以做到 O ( 1 ) O(1) O(1)

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

然后也要把 a i a_i ai加进去,即 N y i , a i = i Ny_{i,a_i}=i Nyi,ai=i

时间复杂度 O ( n log ⁡ 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值