BZOJ4516: [Sdoi2016]生成魔咒

Description

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 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 共有多少种生成魔咒。
 

Input

第一行一个整数 n。
第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符。
1≤n≤100000。,用来表示魔咒字符的数字 x 满足 1≤x≤10^9

Output

输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量

 

Sample Input

7
1 2 3 3 3 1 2

Sample Output

1
3
6
9
12
17
22
 
 
yy万岁!
学SAM时让Rose给我举个例子SAM能干啥,他说求有多少各不同字串用right集合加加减减就可以了
但是这道题每加一个就要跑,真麻烦,不会,我们来yy一下吧!
SAM的性质:对于一个节点u,他能表示的不同字串就是Max(u)-Min(u)+1
其实就是Max(u)-(Min(u)-1)
其中Min(u)-1其实就是Max(parent(u))
为啥??
因为root到parent的每条路径都是到u的后缀,那就可以yy出来了
我们再来感性的yy一下,对于当前节点计算出来的结果是对于当前这个点来说的,比如
AB这个字符串,加入B之后ans就+2,A之前就已经计算过了,所以可以保证正确性(感觉我做SAM全靠YY?)
蒟蒻说话必有失误,如有不对请各路神犇指正
 
代码如下:
#include<map>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
struct SAM{
    int dep,parent;
    map<int ,int > son;
}tr[210000];int last,root,cnt;
typedef long long ll;
ll ans=0ll;
void add(int x,int pos)
{
    int np=++cnt;
    tr[np].dep=pos;
    int p=last;
    while(p!=0&&tr[p].son[x]==0)tr[p].son[x]=np,p=tr[p].parent;
    if(p==0)tr[np].parent=root;
    else
    {
        int q=tr[p].son[x];
        if(tr[q].dep==tr[p].dep+1)tr[np].parent=q;
        else
        {
            int nq=++cnt;
            tr[nq]=tr[q];tr[nq].dep=tr[p].dep+1;
            tr[q].parent=tr[np].parent=nq;
            while(p!=0&&tr[p].son[x]==q)tr[p].son[x]=nq,p=tr[p].parent;
        }
    }
    last=np;
    ans+=(ll)tr[np].dep-tr[tr[np].parent].dep;
}
int main()
{
    int n;
    scanf("%d",&n);
    ans=0;root=last=++cnt;
    for(int i=1;i<=n;i++)
    {
        int x;scanf("%d",&x);
        add(x,i);
        printf("%lld\n",ans);
    }
    return 0;
} 

by_lmy

猜你喜欢

转载自www.cnblogs.com/MT-LI/p/8940558.html