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
这道题有两种做法 sa和sam
做法一 sa
先把整个串翻转过来,然后做后缀数组,那么每次新加一个字符就相当于新加一个后缀..
那么这样就很显而易见的了,题目要求不同字串个数,就用当前字符串总子串个数-所有hei的和.. len*(len+1)/2-sum
用线段树或者splay维护一下sum就好了..
【在刷题01中复制来的】
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define LL long long
#include <set>
using namespace std;
const LL Maxn = 110000;
const LL Max_int = 0x7fffffff;
LL sa[Maxn], rank[Maxn], hei[Maxn];
LL Rsort[Maxn], y[Maxn], wr[Maxn];
LL n;
LL a[Maxn], b[Maxn], bl;
LL bit[Maxn], f[Maxn][20];
LL _min ( LL x, LL y ){ return x < y ? x : y; }
LL _max ( LL x, LL y ){ return x > y ? x : y; }
bool cmp ( LL k1, LL k2, LL ln ){ return wr[k1] == wr[k2] && wr[k1+ln] == wr[k2+ln]; }
void get_sa ( LL m ){
LL i, j, k;
memset ( Rsort, 0, sizeof (Rsort) );
for ( i = 1; i <= n; i ++ ) Rsort[rank[i]] ++;
for ( i = 1; i <= m; i ++ ) Rsort[i] += Rsort[i-1];
for ( i = n; i >= 1; i -- ) sa[Rsort[rank[i]]--] = i;
LL p = 1, ln = 1;
while ( p < n ){
k = 0;
for ( i = n-ln+1; i <= n; i ++ ) y[++k] = i;
for ( i = 1; i <= n; i ++ ){
if ( sa[i]-ln > 0 ) y[++k] = sa[i]-ln;
}
for ( i = 1; i <= n; i ++ ) wr[i] = rank[y[i]];
memset ( Rsort, 0, sizeof (Rsort) );
for ( i = 1; i <= n; i ++ ) Rsort[wr[i]] ++;
for ( i = 1; i <= m; i ++ ) Rsort[i] += Rsort[i-1];
for ( i = n; i >= 1; i -- ) sa[Rsort[wr[i]]--] = y[i];
memcpy ( wr, rank, sizeof (wr) );
p = 1; rank[sa[1]] = 1;
for ( i = 2; i <= n; i ++ ){
if ( !cmp ( sa[i], sa[i-1], ln ) ) p ++;
rank[sa[i]] = p;
}
m = p; ln *= 2;
}
}
void get_hei (){
LL i, j, k;
k = 0;
for ( i = 1; i <= n; i ++ ){
j = sa[rank[i]-1];
if ( k > 0 ) k --;
while ( a[i+k] == a[j+k] ) k ++;
hei[rank[i]] = k;
}
}
LL maxx[Maxn*4], minn[Maxn*4];
void bulid_tree ( LL now, LL L, LL R ){
maxx[now] = 0; minn[now] = Max_int;
if ( L < R ){
LL mid = ( L + R ) >> 1;
bulid_tree ( now*2, L, mid );
bulid_tree ( now*2+1, mid+1, R );
}
}
LL get_max ( LL now, LL L, LL R, LL l, LL r ){
if ( L == l && R == r ) return maxx[now];
LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1;
if ( r <= mid ) return get_max ( lc, L, mid, l, r );
else if ( l > mid ) return get_max ( rc, mid+1, R, l, r );
else return _max ( get_max ( lc, L, mid, l, mid ), get_max ( rc, mid+1, R, mid+1, r ) );
}
LL get_min ( LL now, LL L, LL R, LL l, LL r ){
if ( L == l && R == r ) return minn[now];
LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1;
if ( r <= mid ) return get_min ( lc, L, mid, l, r );
else if ( l > mid ) return get_min ( rc, mid+1, R, l, r );
else return _min ( get_min ( lc, L, mid, l, mid ), get_min ( rc, mid+1, R, mid+1, r ) );
}
void change ( LL now, LL L, LL R, LL x ){
if ( L == R ){
maxx[now] = minn[now] = x;
return;
}
LL mid = ( L + R ) >> 1, lc = now*2, rc = now*2+1;
if ( x <= mid ) change ( lc, L, mid, x );
else change ( rc, mid+1, R, x );
maxx[now] = _max ( maxx[lc], maxx[rc] );
minn[now] = _min ( minn[lc], minn[rc] );
}
LL query ( LL x, LL y ){
LL len = y-x;
return _min ( f[x][bit[len]], f[y-(1<<bit[len])][bit[len]] );
}
int main (){
LL i, j, k;
scanf ( "%lld", &n );
bl = 0;
for ( i = 1; i <= n; i ++ ){
scanf ( "%lld", &a[n-i+1] );
b[++bl] = a[n-i+1];
}
sort ( b+1, b+bl+1 );
bl = unique ( b+1, b+bl+1 ) - (b+1);
for ( i = 1; i <= n; i ++ ){
rank[i] = lower_bound ( b+1, b+bl+1, a[i] ) - b;
}
get_sa (bl);
get_hei ();
bit[1] = 0;
for ( i = 2; i <= n; i ++ ){
if ( (1<<(bit[i-1]+1)) <= i ) bit[i] = bit[i-1]+1;
else bit[i] = bit[i-1];
}
for ( i = 1; i <= n; i ++ ) f[i][0] = hei[i+1];
for ( i = n; i >= 1; i -- ){
for ( j = 1; j <= 19 && i+(1<<j) <= n; j ++ ){
f[i][j] = _min ( f[i][j-1], f[i+(1<<(j-1))][j-1] );
}
}
LL sum = 0;
bulid_tree ( 1, 1, n );
for ( i = n; i >= 1; i -- ){
LL f0 = 0, f1 = 0, f2 = 0;
LL x = rank[i];
LL p1 = get_max ( 1, 1, n, 1, x );
LL p2 = get_min ( 1, 1, n, x, n );
if ( p1 != 0 ) f1 = query ( p1, x );
if ( p2 != Max_int ) f2 = query ( x, p2 );
if ( p1 != 0 && p2 != Max_int ) f0 = query ( p1, p2 );
sum = sum-f0+f1+f2;
printf ( "%lld\n", (n-i+1)*(n-i+2)/2-sum );
change ( 1, 1, n, rank[i] );
}
return 0;
}
做法二 sam
每次在后面加一个数,就在sam加入节点,那么ans+=d[p]-d[fail[p]]就行了
md没改longlong错两次0.0..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <map>
#define LL long long
using namespace std;
const LL Maxn = 100010;
LL F[Maxn*2], d[Maxn*2], tot, now;
map <LL,LL> ch[Maxn*2];
LL n, ans;
LL copy ( LL p, LL c ){
LL x = ++tot, y = ch[p][c];
d[x] = d[p]+1;
ch[x] = ch[y];
F[x] = F[y]; F[y] = x;
while ( ~p && ch[p][c] == y ){ ch[p][c] = x; p = F[p]; }
return x;
}
void add ( LL c ){
LL p, o;
if ( p = ch[now][c] ){
if ( d[p] != d[now]+1 ) copy ( now, c );
now = ch[now][c];
}
else {
d[o=++tot] = d[now]+1; p = now; now = o;
while ( ~p && !ch[p][c] ){ ans += d[p]; ch[p][c] = o; p = F[p]; ans -= d[p]; }
if ( p < 0 ) ans ++;
F[o] = ~p ? ( d[p]+1 == d[ch[p][c]] ? ch[p][c] : copy ( p, c ) ) : 0;
}
}
int main (){
freopen ( "a.in", "r", stdin );
freopen ( "a.out", "w", stdout );
LL i, j, k;
scanf ( "%lld", &n );
F[0] = -1;
ans = 0;
for ( i = 1; i <= n; i ++ ){
LL x;
scanf ( "%lld", &x );
add (x);
printf ( "%lld\n", ans );
}
return 0;
}
可以来对比一下sa和sam
sa:
sam:
很明显的,sa在sam面前弱爆了..