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