码迷,mamicode.com
首页 > 编程语言 > 详细

[COCI2015] Divljak - AC自动机,DFS序,树状数组,LCA

时间:2020-03-08 17:24:36      阅读:65      评论:0      收藏:0      [点我收藏+]

标签:多少   cin   wap   添加   接下来   维护   with   pre   solution   

\(n\) 个询问串 \(S_i\),有一个初始为空的字符串集合 \(T\),接下来有 \(q\) 个操作,每次向集合中添加一个字符串,或给定 \(x\) 询问集合中有多少个字符串包含 \(S_x\)

Solution

考虑对 \(S_i\) 建立 ACAM,建出 \(fail\) 树,一个点发生匹配,则需要修改它到根的链,询问就是查询一个点的值

这样需要树链剖分,我们可以差分一下,那么每次修改的就是一个点,询问的是一个子树的和

于是我们求出 \(fail\) 树的 DFS 序,用树状数组维护,每次修改时,把输入的串扔到 ACAM 上跑,匹配上的结点都 \(+1\),这里需要开个标记数组,防止同一个输入串对同一个结点多次 \(+1\)

每次询问时,就输出 \(S_x\) 对应的子树的子树和即可

很显然上面这个解法是错误的!

虽然我们保证了对于每个询问串,我们只对相同的结点打一次标记,但是前缀和回去的的结果仍然会重复

所以我们需要另一个方法来解决这个路径并的问题

上述解法的基础上,考虑将所有打过标记的点拿出来,按照 \(dfs\) 序排序,对相邻节点的 \(lca\) 位置 \(-1\) 即可

#include <bits/stdc++.h>
using namespace std;

const int N = 2000005;

int ar[N];
int lowbit(int t) {
    return t & (-t);
}
void add(int i, int v) {
    if(i==0) return;
    for (; i < N; ar[i] += v, i += lowbit(i));
}
int sum(int i) {
    int s = 0;
    for (; i > 0; s += ar[i], i -= lowbit(i));
    return s;
}
int sum(int p,int q) {
    return sum(q)-sum(p-1);
}

int dfn[N],idfn[N],fin[N],dep[N],fa[N][21];

int lca(int p,int q) {
    if(dep[p]<dep[q]) swap(p,q);
    for(int i=20;i>=0;--i) if(dep[fa[p][i]]>=dep[q]) p=fa[p][i];
    for(int i=20;i>=0;--i) if(fa[p][i]-fa[q][i]) p=fa[p][i],q=fa[q][i];
    if(p==q) return p;
    else return fa[p][0];
}

struct ACA {
    queue <int> q;
    int c[N][26],val[N],fi[N],cnt,ans[1005];
    void init() {
        memset(c,0,sizeof c);
        memset(val,0,sizeof val);
        memset(fi,0,sizeof fi);
        memset(ans,0,sizeof ans);
        cnt=0;
    }
    int ins(char *str,int id) {
        int len=strlen(str), p=0;
        for(int i=0; i<len; i++) {
            int v=str[i]-'a';
            if(!c[p][v]) c[p][v]=++cnt;
            p=c[p][v];
        }
        val[p]=id;
        return p;
    }
    void build() {
        for(int i=0; i<26; i++) if(c[0][i]) fi[c[0][i]]=0, q.push(c[0][i]);
        while(!q.empty()) {
            int u=q.front();
            q.pop();
            for(int i=0; i<26; i++)
                if(c[u][i]) fi[c[u][i]]=c[fi[u]][i], q.push(c[u][i]);
                else c[u][i]=c[fi[u]][i];
        }
    }
    void query(char *s) {
        set<int> st;
        int len=strlen(s);
        int p=0;
        for(int i=0; i<len; i++) {
            p=c[p][s[i]-'a'];
            if(p&&st.find(dfn[p])==st.end()) {
                st.insert(dfn[p]);
                add(dfn[p],1);
            }
        }
        vector <int> vec;
        for(auto i=st.begin();i!=st.end();i++) vec.push_back(*i);
        for(int i=1;i<vec.size();i++) {
            add(dfn[lca(idfn[vec[i]],idfn[vec[i-1]])],-1);
        }
    }
} acam;

vector<int> g[N];
int n,q,endid[N],ind;
char str[N];

void dfs(int p) {
    dfn[p]=++ind; idfn[ind]=p;
    for(int q:g[p]) fa[q][0]=p, dep[q]=dep[p]+1, dfs(q);
    fin[p]=ind;
}

signed main() {
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1; i<=n; i++) {
        cin>>str;
        endid[i]=acam.ins(str,i);
    }
    acam.build();
    for(int i=1; i<=acam.cnt; i++) g[acam.fi[i]].push_back(i);
    dfs(0);
    for(int j=1;j<=20;j++) {
        for(int i=1;i<=acam.cnt;i++) {
            fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }
    cin>>q;
    for(int i=1; i<=q; i++) {
        int t1,t2;
        cin>>t1;
        if(t1==1) {
            cin>>str;
            acam.query(str);
        } else {
            cin>>t2;
            cout<<sum(dfn[endid[t2]],fin[endid[t2]])<<endl;
        }
    }
}

[COCI2015] Divljak - AC自动机,DFS序,树状数组,LCA

标签:多少   cin   wap   添加   接下来   维护   with   pre   solution   

原文地址:https://www.cnblogs.com/mollnn/p/12443298.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!