题意
给一个序列进行栈操作,从左到右入栈,若当前入栈元素等于栈顶元素则栈顶元素出栈,否则当前元素入栈。若进行完操作后栈为空,这说这个序列是可以被消除的。
给你一个长度为 的序列 ,问 有多少子串是可以被消除的。
题解
定义 表示序列 有多少可以被消除的子串
那么 , 是使得 能够被消除的最小的
如果不存在此 ,那么
那么
考虑如何对于每一个 求出这个
记 表示此 ,那么如果 ,则
否则再判断 ,则
如此递归直到找到一个 或者 为止
这样显然耗时显然是巨大的,而我们每次又只要找
考虑设 表示使得 能够被消除的最小的 且
那么 ,如果 ,那么 跳 就一定会跳到 处再进行判断
那么我们可以直接令 即可(这里 对 使用 可以做到 )
此时 是可以被消除的,所以
然后也要把 加进去,即
时间复杂度
#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;
}