CF1223F Stack Exterminable Arrays 题解

· · 题解

这题是 CSP-S 2023 T2 的加强版。

区间 [l,r] 可以消除当且仅当 a_l=a_r[l+1,r-1] 可消除,或存在一个数 k(l\le k\le r) 满足 [l,k],[k+1,r] 都可消除。

所以一个可消除区间肯定是由若干个左右端点颜色(a_i 是第 i 个点的颜色)相同的可消除连续子区间拼起来的。

考虑对于每个右端点 i 求出最近的左端点使得对应子区间满足条件,记为 l_i。记 f_ii 作为右端点的合法子区间个数,那么答案就是 \sum f_i,易得 f_i=f_{l_i-1}+1

很明显 a_{l_i}=a_i,不然就不满足左端点最靠右。由此显然可得区间 [l_i+1,i-1] 可消除,也就是说从 p=i-1 开始不断地跳 p=l_p-1,最终一定可以跳到 p=l_i

于是可以对每个点用一个 map 维护每种颜色往左边跳到的第一个这种颜色的点的编号。每个点 imap 都需要继承 l_i-1map,可以直接 swap,因为后面 l_i-1map 就不会再用到了。

时间复杂度 O(n\log n)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 300003
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
using namespace std;
int q,n,a[mxn],p[mxn];
ll ans,f[mxn];
map<int,int>mp[mxn];
signed main(){
    scanf("%d",&q);
    while(q--){
        scanf("%d",&n);
        rep(i,1,n)scanf("%d",&a[i]),mp[i].clear(),f[i]=p[i]=0;
        ans=0;
        rep(i,1,n){
            auto it=mp[i-1].find(a[i]);
            if(it!=mp[i-1].end()){
                p[i]=it->second-1;
                f[i]=f[p[i]]+1,ans+=f[i];
                swap(mp[i],mp[p[i]]);
            }
            mp[i][a[i]]=i;
        }
        printf("%lld\n",ans);
    }
    return 0;
}