P4070 [SDOI2016] 生成魔咒
题目描述
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 \(1,2\) 拼凑起来形成一个魔咒串 \([1,2]\)。
一个魔咒串 \(S\) 的非空字串被称为魔咒串 \(S\) 的生成魔咒。
例如 \(S=[1,2,1]\) 时,它的生成魔咒有 \([1],[2],[1,2],[2,1],[1,2,1]\) 五种。\(S=[1,1,1]\) 时,它的生成魔咒有 \([1],[1,1],[1,1,1]\) 三种,最初 S 为空串。
共进行 \(n\) 次操作,每次操作是在 \(S\) 的结尾加入一个魔咒字符。每次操作后都需要求出,当前的魔咒串 \(S\) 共有多少种生成魔咒。
数据规模与约定
对于 \(100\%\) 的数据,保证 \(1 \le n \le 10^5\),\(1 \leq x_i \leq 10^9\)。
说句闲话:
Solution:
十分模板的一道题,每加进来一个数字,我们就在 sam 上将其 insert 那么每次答案统计就是 \(maxlen_x-maxlen_{fa}\)。为什么是这个?因为我们每次在 sam 上 insert 一个数 \(a_i\) 时,会产生 \(i\) 个新串(以 \([1,i]\) 为左端点, \(i\) 为右端点)。那么我们求出fa其实代表着这些串中哪些是旧的(其实就是 rt->fa 能代表的所有串都是重复的,\([minlen_x,maxlen_x]\) 是新的)。二者作差,得到本次新增的答案。
Code:
#include<bits/stdc++.h>
#define ll long long
const int N=2e5+5;
using namespace std;
int n,m;
ll ans;
struct SAM{map<int,int> ch[N];int len[N],fa[N];int last,cnt;void init(){last=cnt=1;}inline void insert(int c){int p=last,q=++cnt;len[q]=len[p]+1;last=q;for(;p&&!ch[p][c];p=fa[p])ch[p][c]=q;if(!p){fa[q]=1;return;}int x=ch[p][c];if(len[x]==len[p]+1){fa[q]=x;return;}int y=++cnt;len[y]=len[p]+1;fa[y]=fa[x];ch[y]=ch[x];for(;p&&ch[p][c]==x;p=fa[p])ch[p][c]=y;fa[x]=fa[q]=y;}inline void calc(){ans+=len[last]-len[fa[last]];}
}sam;
void work()
{cin>>n;sam.init();for(int i=1,x;i<=n;i++){scanf("%d",&x);sam.insert(x);sam.calc();printf("%lld\n",ans);}
}
int main()
{//freopen("incantation.in","r",stdin);freopen("incantation.out","w",stdout);work();return 0;
}