洛谷 P4070 [SDOI2016]生成魔咒 解题报告

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) 共有多少种生成魔咒。

输入输出格式

输入格式:

第一行一个整数 (n)

第二行 (n) 个数,第 (i) 个数表示第 (i) 次操作加入的魔咒字符。

输出格式:

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

输入输出样例

输入样例#1:

7
1 2 3 3 3 1 2

输出样例#1:

1
3
6
9
12
17
22

说明

对于(10\%)的数据,(1 le n le 10)

对于(30\%)的数据,(1 le n le 100)

对于(60\%)的数据,(1 le n le 100)

对于(100\%)的数据,(1 le n le 100000)

用来表示魔咒字符的数字 (x) 满足(1 le x le 10^9)


SAM用map存边

一个状态的贡献是(len[x]-len[par[x]])

可以发现中间生成的节点不计入贡献


Code:

#include <cstdio>
#include <map>
#define ll long long
const int N=2e5+10;
std::map <int,int> ch[N];
int par[N],len[N],las=1,tot=1,n;
ll ans=0;
void extend(int c)
{
    int now=++tot,p=las;
    len[now]=len[p]+1;
    while(p&&!ch[p][c]) ch[p][c]=now,p=par[p];
    if(!p) par[now]=1;
    else
    {
        int x=ch[p][c];
        if(len[x]==len[p]+1) par[now]=x;
        else
        {
            int y=++tot;
            len[y]=len[p]+1,par[y]=par[x],ch[y]=ch[x];
            while(p&&ch[p][c]==x) ch[p][c]=y,p=par[p];
            par[now]=par[x]=y;
        }
    }
    ans=ans+len[now]-len[par[now]];
    las=now;
}
int main()
{
    scanf("%d",&n);
    for(int c,i=1;i<=n;i++)
    {
        scanf("%d",&c);
        extend(c);
        printf("%lld
",ans);
    }
    return 0;
}

2019.1.7

原文地址:https://www.cnblogs.com/butterflydew/p/10231753.html