zoukankan      html  css  js  c++  java
  • 洛谷P2414 [NOI2011]阿狸的打字机

    传送门

    询问y中x出现了多少次,相当于询问y中的节点通过fail指针能到达x的ed节点的有多少个。

    那么一种比较暴力的做法就是,建出AC自动机,把询问按y排个序,遍历trie树,每次暴力跳fail,开个cnt数组维护对每个x的答案。

    考虑y上的点跳fail指针最终到达某个ed点的过程,把fail指针当作边并且反向,发现y上能跳到x的ed的节点都在fail树上x的子树中。如果把fail树建出来,现在只需要考虑怎么统计x的ed点的子树中有多少个属于y的点。

    结合刚刚暴力的做法,发现遍历trie树走到y的ed节点时,栈内保存的经过点就是y串。也就是说,在走到y的ed点时,可以做到将y上的所有点都标记出来,并且只标记y上的点。这个时候对于一个询问(x,y),只需要求fail树上x的ed点的子树中有多少个被标记出来的点。dfs一遍fail树,将每个点映射到dfs序。子树的dfs序是连续的,于是问题就变成了询问一段连续区间内的值,树状数组维护即可。

    对于trie树上的每个点开个vector记录它是哪个串的ed点,遍历到一个点的时候处理以它结尾的串的询问。这里的做法没有排序询问,而把每个询问存入y的vector中,处理每个串的时候遍历这个vector。

    时间复杂度是O(nlogn)。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    using namespace std;
    const int N=1e5+10;
    char s[N];
    int fa[N],lens,now,tree[N][26],tree1[N][26],num,ed[N],tot,m,fail[N],ans[N];
    struct node{
        int x,id;
        node(int a=0,int b=0){
            x=a,id=b;
        }
    };
    vector<node>v[N];
    vector<int>e[N];
    void insert(){
        for(int j=1;j<=lens;j++){
            if(s[j]=='B')now=fa[now];
            else if(s[j]=='P'){
                num++;
                ed[num]=now;
                e[now].push_back(num);
            }
            else{
                if(!tree[now][s[j]-'a'])tree1[now][s[j]-'a']=tree[now][s[j]-'a']=++tot,fa[tot]=now;
                now=tree[now][s[j]-'a'];
            }
        }
    }
    int Next[N],head[N],tot1,ver[N];
    void add(int x,int y){
        ver[++tot1]=y;
        Next[tot1]=head[x];
        head[x]=tot1;
    }
    queue<int>q0;
    void getfail(){
        for(int i=0;i<26;i++){
            if(tree[0][i]){
                add(0,tree[0][i]);
                q0.push(tree[0][i]);
            }
        }
        while(q0.size()){
            int u=q0.front();
            q0.pop();
            for(int i=0;i<26;i++){
                int v=tree1[u][i];
                if(v){
                    fail[v]=tree1[fail[u]][i];
                    add(fail[v],v);
                    q0.push(v);
                }
                else tree1[u][i]=tree1[fail[u]][i];
            }
        }
    }
    int tim,rec[N],rec1[N];
    void dfs(int x){
        rec[x]=++tim;
        for(int i=head[x];i;i=Next[i]){
            int y=ver[i];
            dfs(y);
        }
        rec1[x]=tim;
    }
    int tr[N];
    void add1(int x,int val){
        for(;x<=tim;x+=(x&-x))tr[x]+=val;
    }
    int ask(int x){
        int sum=0;
        for(;x;x-=(x&-x))sum+=tr[x];
        return sum;
    }
    void solve(int x){
        for(int i=0;i<v[x].size();i++){
            node y=v[x][i];
            ans[y.id]=ask(rec1[ed[y.x]])-ask(rec[ed[y.x]]-1);
        }
    }
    void dfs1(int x){
        if(e[x].size()){
            for(int i=0;i<e[x].size();i++){
                int y=e[x][i];
                solve(y);
            }
        }
        for(int i=0;i<26;i++){
            if(tree[x][i]){
                add1(rec[tree[x][i]],1);
                dfs1(tree[x][i]);
                add1(rec[tree[x][i]],-1);
            }
        }
    }
    int main()
    {
        scanf("%s",s+1);
        lens=strlen(s+1);
        insert();
        getfail();
        dfs(0);
        scanf("%d",&m);
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            v[y].push_back(node(x,i));
        }
        dfs1(0);
        for(int i=1;i<=m;i++)printf("%d
    ",ans[i]);
        return 0;
     } 
    P2414 [NOI2011]阿狸的打字机

    ·建立trie树的时候直接根据操作在树上走动就好了。操作是小写字母的话就往儿子走一步,操作B则往父亲走一步,操作P的话记录当前点为当前串的ed节点并把当前串丢进节点的vector里去。用不着每次暴力插入一个新串,正解被我硬生生搞成70分暴力…

    ·注意映射关系的细节。

    ·由于最后还要遍历一遍trie树,前面getfail的时候不能把原trie树建成trie图,可以备份一棵trie树用来getfail。

  • 相关阅读:
    【网络安全】十三步简单入侵个人电脑教程
    [深入学习Web安全](11)之XSS玩法
    W3bsafe]SQLmap过狗命令的利用+教程
    Python 爬虫修养-处理动态网页
    WEB站点服务器安全配置
    jboss final 7.1.1相关error以及解决方式
    “System.IO.FileNotFoundException”类型的未经处理的异常在 mscorlib.dll 中发生
    HDU 2196 Computer 树形DP经典题
    python3连接Mairadb数据库
    jQuery动画animate()的使用
  • 原文地址:https://www.cnblogs.com/chloris/p/12032927.html
Copyright © 2011-2022 走看看