bzoj4516: [Sdoi2016]生成魔咒

14 篇文章 0 订阅
1 篇文章 0 订阅

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面前弱爆了..

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值