题解 CF653F Paper task

CF653F Paper task

给定一个长度为 \(n\) 和括号串,求本质不同的合法括号串个数。\(n\le 5\times 10^5\)

考虑如果不是求本质不同,可以想到 DP。

\(f_{i}\) 表示以 \(i\) 结尾的括号串数,容易发现 \(f_{i}=f_{t_{i}-1}+1\),其中 \(t_{i}\) 表示与 \(i\) 匹配的左括号位置。用栈模拟即可做到 \(O(n)\)。我们考虑把这个转移的边建出来,然后发现这是一个森林的结构。

再考虑去重,利用 SA,对于每个数的 \(f_{i}\),我们只取长度严格大于 \(height_{i}\) 的串,这对于森林中就是到根到该点路径的一段前缀,倍增优化跳的过程即可做到 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const ll maxn=5e5+5;
char s[maxn];

ll n, m=100;
ll num[maxn], rk[maxn], sa[maxn], tp[maxn], height[maxn], g[maxn];

void basesort(){
    memset(num,0,sizeof(num));
    for(ll i=1;i<=n;i++) num[rk[i]]++;
    for(ll i=1;i<=m;i++) num[i]+= num[i-1]; 
    for(ll i=n;i>=1;i--) sa[num[rk[tp[i]]]--]=tp[i];
    return ;
}

void SuffixSort() {
	m=100s;
    for(ll i=1;i<=n;i++) rk[i]=s[i]-'('+1,tp[i]=i; 
    basesort();
    for(ll w=1,p=0;p<n;m=p,w<<=1) {
        p=0;
        for(ll i=1;i<=w;i++) tp[++p]=n-w+i;
        for(ll i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
        basesort();
        for(ll i=1;i<=n;++i) swap(tp[i],rk[i]);
        rk[sa[1]]=1; 
		p=1;
        for(ll i=2;i<=n;i++) {
        	if(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w]) rk[sa[i]]=p;
            else rk[sa[i]]=++p;
        }
    }
    return ;
}

ll f[maxn][20];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin>>n;
    for(ll i=1;i<=n;i++) cin>>s[i];
    for(ll i=1;i<=n+1;i++) {
        for(ll j=0;j<20;j++) f[i][j]=n+1;
    }
    vector<ll> vec;
    for(ll i=n;i>=1;i--) {
        if(s[i]==')') vec.push_back(i);
        else {
            if(vec.size()&&s[vec.back()]==')') {
                f[i][0]=vec.back()+1;
                g[i]=g[vec.back()+1]+1;
                for(ll j=1;j<20;j++) f[i][j]=f[f[i][j-1]][j-1];
                vec.pop_back();
            }else vec.push_back(i);
        }
    }
    SuffixSort();
    ll k=0;
    for(ll i=1;i<=n;i++) {
        if(k) --k;
        ll j=sa[rk[i]-1];
        while(i+k<=n&&s[i+k]==s[j+k]) ++k;
        height[rk[i]]=k;
    }
    ll ans=0;
    for(ll i=1;i<=n;i++) {
        ll x=sa[i];
        for(ll j=19;j>=0;j--) if(f[x][j]-sa[i]<=height[i]) x=f[x][j];
        ans+=g[x];
    }
    cout<<ans<<'\n';
    return 0;
}
posted @ 2024-01-18 11:59  OIshima  阅读(12)  评论(0编辑  收藏  举报