BZOJ 3881 [Coci2015]Divljak

AC自动机+fail树+树链求并+树状数组

挺复杂的一道题。

看到这题的第一反应是把S里的串全都丢到AC自动机里,然后对于T中的每一个串在AC自动机上走,走到每一个节点时暴力一直往回跳fail来贡献答案。由于fail指针可能有很多,应该是可以卡到O(n^2)的,过不了吧。

我们发现每一次贡献答案都是沿着fail边往上的,于是用fail树来考虑。每一次一个节点贡献答案相当于该节点到根的路径全都贡献答案。最终贡献答案的形式应当是若干条树链的并都加上1。

转化为做树链的并:按DFS序排序,每个点到根的路径上的所有节点权值+1,相邻两个点的LCA到根的路径上的所有节点权值-1。(对这个方法不理解的可以手动画图,能对这个方法有一个感性的认识)

转化为树链加值的问题,用树链剖分+线段树肯定T。转化为差分,那么一个节点的答案就是子树和了。用树状数组就可以。

运气挺好,写下来没有什么重大的错误, 还是被一些小问题卡了很久。。。毕竟我弱。。。

#include<cstdio> 
#include<algorithm> 
#define N 100005
#define L 2000005
#define H 23
#define lowbit(_i) (_i&-_i)
using namespace std;
namespace ziqian
{
    const int INF = 1<<30;
    int n, ecnt, pos[N], last[L], total, dfn[L], dfnn[L], beg[L], end[L], dfn_timer, timer, list[L<<1], log[L<<1], bin[H], dep[L], ST[L<<1][H];
    char s[L];
    struct edge{int next, to;}e[L];
    void addedge(int a, int b)
    {
        e[++ecnt] = (edge){last[a], b};
        last[a] = ecnt; 
    }
    void dfs(int x)
    {
        list[++timer] = x;
        beg[x] = timer;
        dfn[x] = ++dfn_timer;
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to;
            dep[y] = dep[x] + 1; 
            dfs(y);
            list[++timer] = x;
        }
        end[x] = timer;
        dfnn[x] = dfn_timer;
    }
    inline int dep_min(int a, int b){return dep[a]<dep[b] ? a:b;}
    void build_ST()
    {
        dep[0] = INF; bin[0] = 1;
        for(int i = 1; i < H; i++)bin[i] = bin[i-1] << 1;
        for(int i = 2; i <= timer; i++)log[i] = log[i>>1] + 1;
        for(int i = 1; i <= timer; i++)ST[i][0] = list[i];
        for(int h = 1; h < H; h++) for(int i = 1; i+bin[h]<=timer; i++)
        {
            ST[i][h] = dep_min(ST[i][h-1], ST[i+bin[h-1]][h-1]);
        }
    }
    int ask_lca(int a, int b)
    {
        a = beg[a], b = beg[b];
        if(a > b)swap(a, b);
        int len = b-a+1;
        return dep_min(ST[a][log[len]], ST[b-bin[log[len]]+1][log[len]]);
    }

    struct BITree
    {
        int t[L];
        void add(int x, int val){while(x <= dfn_timer)t[x] += val, x += lowbit(x);}
        int ask(int x){int sum = 0; while(x) sum += t[x], x -= lowbit(x); return sum;}
    }BIT;
    struct node
    {
        node *fail, *ch[26];
    }mempool[L], *tot, *q[L];
    bool cmp(node* a, node* b){return dfn[a-mempool] < dfn[b-mempool];}
    struct ACAM
    {
        node *root;
        void init()
        {
            tot = mempool;
            root = ++tot;
        }
        void insert(int id)
        {
            node *p = root;
            for(int i = 0; s[i]; i++)
            {
                int c = s[i]-'a';
                if(!p->ch[c])p->ch[c] = ++tot;
                p = p->ch[c];
            }
            pos[id] = p - mempool;
        }
        void build()
        {
            total = tot - mempool;
            int head=0, tail=0;
            for(int c = 0; c < 26; c++) 
            {
                if(root->ch[c])q[tail++] = root->ch[c], root->ch[c]->fail = root;
                else root->ch[c] = root;
            }
            for(; head<tail; head++)
            {
                node *x = q[head];
                for(int c = 0; c < 26; c++)
                {
                    if(x->ch[c]) q[tail++] = x->ch[c], x->ch[c]->fail = x->fail->ch[c];
                    else x->ch[c] = x->fail->ch[c]; 
                }
            }
            for(int i = 0; i < tail; i++)
                addedge(q[i]->fail - mempool, q[i] - mempool);
        } 
        int solve()
        {
            node *p = root;
            int tail = 0;
            for(int i = 0; s[i]; i++)
            {
                int c = s[i] - 'a';
                p = p->ch[c];
                if(p != root)q[++tail] = p;
            }
            return tail;
        }
    }AC;

    void main()
    {
        AC.init();
        scanf("%d",&n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%s",s);
            AC.insert(i);
        }
        AC.build();
        dfs(1);
        build_ST();
        int Q; scanf("%d",&Q);
        for(; Q--; )
        {
            int opt, x;
            scanf("%d",&opt);
            if(opt == 1)
            {
                scanf("%s",s);
                int tail = AC.solve();
                sort(q+1, q+1+tail, cmp);
                for(int i = 1; i <= tail; i++)
                {
                    BIT.add(dfn[q[i]-mempool], 1);
                    if(i != 1) BIT.add(dfn[ask_lca(q[i] - mempool, q[i-1]- mempool)], -1);
                }
            }
            else
            {
                scanf("%d",&x);
                int tmp1 = BIT.ask(dfn[pos[x]]-1), tmp2 = BIT.ask(dfnn[pos[x]]);
                printf("%d\n",tmp2 - tmp1);
            }
        }
    }
}
int main()
{
    ziqian::main();
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值