zoukankan      html  css  js  c++  java
  • BZOJ 4545 DQS的Trie

    BZOJ 4545 DQS的Trie

    第一眼,这不是很裸嘛?

    直接构造广义SAM然后跑就行了啊。

    动态询问 endpos 集合大小,LCT就好了嘛

    (码...)

    码了一半,然后发现,每次新加入一个子树啊,复杂度是假的啊?

    那这么说网上很多题解貌似都是假的。。。

    (你按照dfs的顺序来加,不是就必然会被卡了嘛)

    但是这个题不强制在线!

    可以先把整个树搭建好再处理询问。

    考虑我们加入一个字符,其实是在让一些 SAM 上的节点 从 “没出现过” 变成 “出现过”

    考虑在parent树上,可以直接跳,因为一个节点只会从没出现过变成出现过一次,是 $ O(n) $ 的

    这样就可以方便的维护第一个东西啦

    当然,加入一个字符后就把这个点在parent树上的祖先全部+1,这样就维护好第三个操作啦

    祖先+1可以看作单点+1,区间查询,可以用BIT维护

    复杂度带个BIT的log,不卡常都2000ms跑过去。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    using namespace std;
    typedef long long ll;
    #define MAXN 500006
    
    int n , lst , m;
    
    struct SAM{
        int son[MAXN][27]; // sons
        int par[MAXN] , len[MAXN]; // node
        int cnt;
        int sz[MAXN];
        int head[MAXN] , to[MAXN << 1] , nex[MAXN << 1] , ecn;
        ll num;
    
        void init(  ) {
            memset( son , 0 , sizeof son ); memset( head , 0 , sizeof head );
            cnt = lst = 1; ecn = 0;
        }
        void ade( int u , int v ) {
            nex[++ecn] = head[u] , head[u] = ecn , to[ecn] = v;
        }
        void addall( ) {
            for( int i = 2 ; i <= cnt ; ++ i ) ade( par[i] , i );
        }
        void ins( int x ) {
            int cur = ++ cnt;
            len[cur] = len[lst] + 1 , sz[cur] = 1;
            int p = lst;
            while( p && !son[p][x] ) son[p][x] = cur , p = par[p];
            if( !p ) par[cur] = 1;
            else {
                int q = son[p][x];
                if( len[q] == len[p] + 1 ) par[cur] = q;
                else {
                    int cl = ++ cnt;
                    memcpy( son[cl] , son[q] , sizeof son[q] );
                    par[cl] = par[q]; // copy
                    len[cl] = len[p] + 1 , par[q] = par[cur] = cl;
                    for( ; son[p][x] == q ; p = par[p] ) son[p][x] = cl;
                }
            }
            lst = cur;
        }
        int L[MAXN] , R[MAXN] , dfn;
        void dfs( int u ) {
            L[u] = ++ dfn;
            for( int i = head[u] ; i ; i = nex[i] )
                dfs( to[i] );
            R[u] = dfn;
        }
        int vis[MAXN];
        int jump( int u ) {
            if( vis[u] ) return 0;
            vis[u] = 1;
            return len[u] - len[par[u]] + jump( par[u] );
        }
    
        int T[MAXN];
        void add( int u , int c ) {
            while( u < MAXN ) T[u] += c , u += ( u & -u );
        }
        int sum( int u ) {
            int ret = 0;
            while( u > 0 ) ret += T[u] , u -= ( u & -u );
            return ret;
        }
    
        void getit( int u ) {
    //        printf("get : %d
    ",u);
            num += jump( u );
            add( L[u] , 1 );
        }
    
        int mach( char* c , int l ) {
            int cur = 1;
            for( int i = 0 ; i < l ; ++ i ) {
                if( !son[cur][c[i] - 'a'] ) return 0;
                else cur = son[cur][c[i] - 'a'];
            }
            return sum( R[cur] ) - sum( L[cur] - 1 );
        }
    } S ;
    
    int head[MAXN] , to[MAXN << 1] , nex[MAXN << 1] , wt[MAXN] , ecn;
    void ade( int u , int v , int w ) {
        to[++ ecn] = v , nex[ecn] = head[u] , head[u] = ecn , wt[ecn] = w;
    }
    int pos[MAXN] , fa[MAXN];
    vector<int> ps[MAXN]; int cni;
    void bfs( int u ) {
        queue<int> Q;
        Q.push( u );
        while( !Q.empty( ) ) {
            int x = Q.front(); Q.pop();
            for( int i = head[x] ; i ; i = nex[i] ) {
                int v = to[i];
                if( fa[x] == v || fa[v] ) continue;
                fa[v] = x;
                lst = pos[x];
                S.ins( wt[i] );
                pos[v] = lst , Q.push( v );
                ps[cni].push_back( v );
            }
        }
    }
    
    char ch[MAXN]; int bk[MAXN] , ln[MAXN] , id , tot;
    char op[MAXN];
    struct query {
        int opt , id;
    } Q[MAXN] ;
    
    int main() {
        scanf("%*d");
        cin >> n;
        for( int i = 1 , u , v ; i < n ; ++ i ) {
            scanf("%d%d%s",&u,&v,ch);
            ade( u , v , ch[0] - 'a' ) , ade( v , u , ch[0] - 'a' );
        }
        S.init(); pos[1] = 1;
        bfs( 1 );
        cin >> m;
        int opt , rt , s;
        for( int i = 1 ; i <= m ; ++ i ) {
            scanf("%d",&opt);
            if( opt == 1 ) {
                Q[i].opt = 1;
            } else if( opt == 2 ) {
                Q[i].opt = 2;
                ++ cni;
                scanf("%d%d",&rt,&s);
                for( int i = 1 , u , v ; i < s ; ++ i ) {
                    scanf("%d%d%s",&u,&v,ch);
                    ade( u , v , ch[0] - 'a' ) , ade( v , u , ch[0] - 'a' );
                }
                bfs( rt );
                Q[i].id = cni;
            } else {
                scanf("%s",op + tot);
                bk[i] = tot , ln[i] = strlen( op + tot );
                tot += ln[i];
                Q[i].opt = 3;
            }
        }
        Q[0].opt = 2;
        S.addall();
        S.dfs( 1 );
    
        for( int i = 0 ; i <= m ; ++ i ) {
            int o = Q[i].opt;
            if( o == 1 ) {
                printf("%lld
    ",S.num);
            } else if( o == 2 ) {
                int c = Q[i].id;
                for( int j = 0 ; j < ps[c].size() ; ++ j ) {
                    int u = ps[c][j];
                    S.getit( pos[u] );
                }
            } else {
                printf("%d
    ",S.mach( op + bk[i] , ln[i] ));
            }
        }
    }
    
  • 相关阅读:
    线程间协作的两种方式:wait、notify、notifyAll和Condition
    Lock
    线程池ExecutorService的使用
    使用volatile的条件
    解决共享资源竞争
    并发时捕获异常
    Executor执行器
    BufferedReader 和BufferedWriter
    彻底理解Java的Future模式
    贝叶斯网络简介--翻译版
  • 原文地址:https://www.cnblogs.com/yijan/p/bzoj4545.html
Copyright © 2011-2022 走看看