[bzoj4650][Noi2016]优秀的拆分——后缀数组

题目大意:

定义一个字符串的拆分是优秀的当且仅当是\(AABB\)的形式,求给定字符串的所有子串的所有的拆分中有多少是优秀的。

思路:

95分太好拿了,这里直接考虑正解该怎么做。

不难发现我们只需要求出每个点开头的\(AA\)形式的字符串和每个点结尾的\(AA\)字符串,然后枚举分割点两边乘起来就好了。可是关键是\(AA\)形式的字符串可能有\(n^2\)个,直接枚举的话一定不是正解。

考虑分长度来处理所有的这种子串,对于长度为\(2\times len\)的\(AA\)形式的子串,我们将原串每隔\(len\)就建立一个关键点,不难发现所有长度为\(2\times len\)的子串必定可以表示为两个关键点前面接一个lcs,后面接一个lcp,并且lcs和lcp的部分有重叠,重叠的部分就是可以作为两个\(A\)分割点的地方,得到了分割点,\(AA\)的起点和终点的范围也就知道了。

于是我们只需要枚举每两个关键点,然后实现区间加法,单点查询即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.6
 * Problem : luogu1117
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;

using namespace std;

void File(){
    freopen("luogu1117.in","r",stdin);
    freopen("luogu1117.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T fl=1; char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
    for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0');
    _*=fl;
}

const int maxn=3e4+10;
int T,n,Log[maxn];

struct Suffix_Array{
    char s[maxn];
    int sz,sa[maxn],rk[maxn],tp[maxn],tax[maxn],height[maxn];
    int st[maxn][15];
    void radix_sort(){
        REP(i,1,sz)tax[i]=0;
        REP(i,1,n)++tax[rk[i]];
        REP(i,1,sz)tax[i]+=tax[i-1];
        DREP(i,n,1)sa[tax[rk[tp[i]]]--]=tp[i];
    }
    void suffix_sort(){
        memset(rk,0,sizeof(rk));
        memset(sa,0,sizeof(sa));
        memset(tp,0,sizeof(tp));
        sz=26;
        REP(i,1,n)rk[i]=s[i]-'a'+1,tp[i]=i;
        radix_sort();
        for(int w=1,p=0;w<n;w<<=1){
            p=0;
            REP(i,1,w)tp[++p]=n-w+i;
            REP(i,1,n)if(sa[i]>w)tp[++p]=sa[i]-w;
            radix_sort();
            swap(rk,tp);
            rk[sa[1]]=p=1;
            REP(i,2,n)
                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;
            sz=p;
            if(sz==n)break;
        }
        int p=0;
        REP(i,1,n){
            if(p)--p;
            int j=sa[rk[i]-1];
            while(s[i+p]==s[j+p])++p;
            height[rk[i]]=p;
        }
        REP(i,1,n)st[i][0]=height[i];
        REP(j,1,Log[n])
            REP(i,1,n-(1<<j)+1)
            st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    }
    int lcp(int x,int y){
        x=rk[x],y=rk[y];
        if(x>y)swap(x,y);
        ++x;
        int d=Log[y-x+1];
        return min(st[x][d],st[y-(1<<d)+1][d]);
    }
}S[2];

ll pre[maxn],nex[maxn];

void work(){
    memset(pre,0,sizeof(pre));
    memset(nex,0,sizeof(nex));
    REP(len,1,n/2){
        if(len==1){
            REP(i,1,n-1)if(S[0].s[i]==S[0].s[i+1]){
                ++pre[i],--pre[i+1];
                ++nex[i+1],--nex[i+2];
            }
            continue;
        }
        for(int i=len*2;i<=n;i+=len){
            int j=i-len;
            int l=max(j-S[1].lcp(n-j+1,n-i+1)+1,j-len+1);
            int r=min(j+S[0].lcp(j,i)-len,j);
            //debug(j),debug(i)<<endl;
            if(l<=r){
                ++pre[l],--pre[r+1];
                //cout<<l<<" "<<r<<endl;
                l+=len*2-1,r+=len*2-1;
                ++nex[l],--nex[r+1];
                //cout<<l<<" "<<r<<endl;
            }
        }
    }
    REP(i,1,n){
        pre[i]+=pre[i-1];
        nex[i]+=nex[i-1];
    }
    /*REP(i,1,n)printf("%lld ",pre[i]);
    printf("\n");
    REP(i,1,n)printf("%lld ",nex[i]);
    printf("\n");*/
    ll ans=0;
    REP(i,1,n-1)ans+=nex[i]*pre[i+1];
    printf("%lld\n",ans);
}

int main(){
//  File();
    REP(i,2,3e4)Log[i]=Log[i>>1]+1;
    read(T);
    while(T--){
        scanf("%s",S[0].s+1);
        strcpy(S[1].s+1,S[0].s+1);
        n=strlen(S[0].s+1);
        reverse(S[1].s+1,S[1].s+n+1);
        S[0].suffix_sort();
        S[1].suffix_sort();
        work();
    }
    return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10354879.html

时间: 2024-07-30 16:01:58

[bzoj4650][Noi2016]优秀的拆分——后缀数组的相关文章

bzoj4650: [Noi2016]优秀的拆分

考场上没秒的话多拿5分并不划算的样子. 思想其实很简单嘛. 要统计答案,求以每个位置开始和结束的AA串数量就好了.那么枚举AA中A的长度L,每L个字符设一个关键点,这样AA一定经过相邻的两个关键点.计算出相邻关键点的最长公共前后缀,把对应的位置区间加一下. 求lcp和lcs可以用后缀数组,也可以用hash. #include<bits/stdc++.h> #define N 30005 #define M (l+r+1>>1) using std::min; const int m

[Noi2016]优秀的拆分

[Noi2016]优秀的拆分 题目 如果一个字符串可以被拆分为 AABB的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的.例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式.一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分.比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串:但是,字符串 abaabaa 就没有优秀的拆分.现在给出一个长度为 n 的字符串 S,我们需要求出,在

BZOJ 4650 [Noi2016]优秀的拆分:后缀数组

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4650 题意: 给你一个字符串s,问你s及其子串中,将它们拆分成"AABB"的方式共有多少种. 题解: 先只考虑"AA"的形式. 设pre[i]表示以s[i]结尾的"AA"串共有多少个,nex[i]表示以s[i]开头的"AA"串共有多少个. 那么拆分成"AABB"的总方案数 = ∑ pre[i]*ne

bzoj 4650: [Noi2016]优秀的拆分

传送门 随便后缀数组跑一跑n^2就有95分,简直不能再划算啊. 正解如图.然后差分就好了. 灵魂画手. 这两天bug奇多,难得1A,老泪纵横. 原文地址:https://www.cnblogs.com/Achenchen/p/8343909.html

后缀数组小结?

做了一圈(就那么几道还叫一圈)$SA$的题,小结一下,方便自己看 [NOI2016]优秀的拆分 1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define mem(x) memset((x),0,sizeof((x))) 6 struct SA{ 7 char s[60005]; 8 int n,m; 9 int t1[60005],t2[600

【NOI2016】优秀的拆分

题目描述 如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式. 一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分.比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串:但是,字符串 abaabaa 就没有优秀的拆分. 现在给出一个长度为 n的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,

字符串(后缀自动机):NOI 2016 优秀的拆分

[问题描述] 如果一个字符串可以被拆分为 AABB 的形式,其中 A 和 B 是任意非空字符串, 则我们称该字符串的这种拆分是优秀的. 例如,对于字符串 aabaabaa,如果令 A = aab, B = a, 我们就找到了这个字符串拆分成 AABB 的一种方式. 一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分. 比如我们令 A = a, B = baa,也可以用 AABB 表示出上述字符串:但是,字符串abaabaa 就没有优秀的拆分. 现在给出一个长度为 n 的字符串 S,我们需

后缀数组之hihocoder 重复旋律1-4

蒟蒻知道今天才会打后缀数组,而且还是nlogn^2的...但基本上还是跑得过的: 重复旋律1: 二分答案,把height划分集合,height<mid就重新划分,这样保证了每个集合中的LCP>=mid,套路板子题 // MADE BY QT666 #include<cstdio> #include<algorithm> #include<cmath> #include<iostream> #include<cstring> using

后缀数组入门

基础介绍: http://www.nocow.cn/index.php/%E5%90%8E%E7%BC%80%E6%95%B0%E7%BB%84 应用:整理自<后缀数组--处理字符串的有力工具> 2.1.最长公共前缀 这里先介绍后缀数组的一些性质. height数组:定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀.那么对于j和k,不妨设rank[j]<rank[k],则有以下性质: suffix(j