【bzoj4516】[Sdoi2016]生成魔咒

4516: [Sdoi2016]生成魔咒

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1362   Solved: 766
[ Submit][ Status][ Discuss]

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

HINT

Source

[ Submit][ Status][ Discuss]



get到一个新姿势:SAM上节点u所代表的本质不同的子串的个数等于Max[u] - Max[fa[u]]

那么接下来就很好搞了,直接边插入边维护就好

字母表用Map储存

代码:
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<algorithm>
#include<cstdlib>
#include<stack>
#include<cstring>
#include<map>
#include<cmath>
using namespace std;

typedef long long LL;

const int INF = 2147483647;
const int maxn = 100100;

int a[maxn];
map<int,int> ch[maxn * 2];
int f[maxn * 2],Max[maxn * 2],dp[maxn * 2],cnt[2 * maxn],tot = 1,last = 1;
LL n,ans;

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline LL cal(int u)
{
	return Max[u] - Max[f[u]];
}

inline void insert(int c)
{
	int v = last;
	Max[last = ++tot] = Max[v] + 1; f[last] = 1;
	while (v && !ch[v][c]) ch[v][c] = last , v = f[v];
	if (!v) {ans += cal(last); return;}
	int p = ch[v][c];
	if (Max[p] != Max[v] + 1)
	{
		int np = ++tot;
		Max[np] = Max[v] + 1; 
		f[np] = f[p]; ans += cal(np); 
		ans -= cal(p); f[p] = np; ans += cal(p);
		f[last] = np;
		while (v && ch[v][c] == p) ch[v][c] = np , v = f[v];
		ch[np] = ch[p];
	}
	else f[last] = p;
	ans += cal(last);
}

int main()
{
	n = getint();
	for (int i = 1; i <= n; i++) a[i] = getint();
	for (int i = 1; i <= n; i++) insert(a[i]) , printf("%lld\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值