HDU 5790 Prefix(字典树、主席树)

来源:互联网 发布:js 鼠标的位置 编辑:程序博客网 时间:2024/06/10 07:21

前置技能

  1. 主席树 (模仿kuangbin,不用递归用while)

  2. 题目 spoj3290 传送门君

    给一串数列,有q个(1e5的数量级)询问,求i到j间的不同数字的个数

题意

N个串,Q个询问。询问第[L,R]的串里面有多少个不同的前缀1<=N<=100000 ,Q<=1e5,串的总长度<=1e5

思路

主席树 + Trie每次插入一个串s[i],相当于新建一棵线段树T[i]。(主席树记录历史状态)。在spoj3290中,T[i]中记录的是[l,r]区间内(1<=l<=r<=i),a[l...r]中不同数字的个数。而这题中,T[i]维护的是,对于字符串数组s[maxn],s[l]...s[r]中有多少不同的前缀。怎样维护的呢?我实现的主席树中,记c[o]为线段树的端点,o是一个区间的编号。这个c[o]维护的是什么呢?若o代表的区间是[j,j],即第j个串(第T[i]个线段树中)。记这些o为o1,o2,....(第1个串,第2个串...)记录的就是这个串s[j]中,最后一次出现的前缀个数。(至于为什么要记录最后一次出现,参考spoj3290的思路)举个栗子(每次插入字符串到Trie中)

这里写图片描述

第一棵线段树中,Tree[1]的c[o1]+=3
第二棵线段树中, Tree[2]的c[o2]+=2
第三棵线段树问题来了,”abd”的三个前缀都是最后一次出现,所以Tree[3]的c[o3]+=3,但”a”,”ab”在之前的第一棵线段树中已经出现并记录过了,所以要把Tree[3]的c[o1]-=2;
第四棵树,Tree[4]的c[o4]+=4,那么第3棵树中的”ab”与“a”都不是最后一次出现了,Tree[4]的c[o3] -=2。第1棵树中的”abc”也不再是最后一次出现了。所以Tree[4]的c[o1]-=1;第1棵树里也有”ab”,要不要c[o1]再-=2呢?不需要。因为之前插入第3个串的时候,已经减去过了,不必重复再减。
所以说插入一个串i的时候,必然有Tree[i]的c[oi]+=strlen(s[i])
但应该减去Tree[i]的哪些c[o]呢,减去多少呢
所以用一个v[]记录Trie中的每个字符最后一次出现在哪个字符串里。见图。则对于插入的新串中的每个字符ch,那么就让Tree[i]的c[o v[ch] ]–;
例如第4棵树,插入“abce”,插入Trie的时候,第一个a的v为3,那么Tree[4]的c[o3]– 然后v[这个’a’]更新为4。v[‘b’]为3,Tree[4]的c[o3]– 然后v[这个’b’]更新为4。v[‘c’]为1,Tree[4]的c[o1]–; 然后v[这个’c’]更新为4。新建结点e, v[‘e’]就赋值为4
这样查询[L,R]的时候,就对于Tree[R]的c[L..R]求和就行了

坑点

  1. 用指针实现Trie会MLE,改为数组实现
  2. 主席树开15 * maxn还是会re ,开30才过(按理说应该开40吧)

代码

#include <vector>#include<cstdio>#include<cstring>#include<algorithm>#include <iostream>#include <stack>#include <cmath>#include <map>#include <set>#include <queue>#include <deque>using namespace std;typedef long long ll;#define MAX 26const int maxn = 1e5+10;const int maxm = 30 * maxn;int N,Q;//-----改掉Trie-----/int nextt[maxn][MAX];int v[maxn];int totTrie;int tot;int a[maxn];int T[maxn], lson[maxm], rson[maxm], c[maxm];int build(int l,int r){    int root = tot ++;    c[root] = 0;    if(l!=r){        int mid = (l+r) >> 1;        lson[root] = build(l,mid);        rson[root] = build(mid+1 ,r);    }    return root;}int Insert(int root,int pos,int val){    int newroot = tot++, tmp = newroot;    int l = 1, r = N;    c[newroot] = c[root] + val;    while( l < r){        int mid = (l + r) >> 1;        if(pos <=mid){            lson[newroot] = tot++;rson[newroot] = rson[root];            newroot = lson[newroot]; root = lson[root];            r = mid;        }else {            rson[newroot] = tot++; lson[newroot] = lson[root];            newroot = rson[newroot]; root = rson[root];            l = mid + 1;           }        c[newroot] = c[root] +val;    }    return tmp;}int Query(int root, int p, int l, int r){    if(l == p) return c[root];    int mid = (l+r) >> 1;    if( p <= mid) return Query(lson[root], p, l, mid) + c[rson[root]];    return Query(rson[root], p, mid+1, r);}int now;//现在插入的是第几棵树void InsertTrie(){    char ch;    int len = 0;    int p = 0, q;    while((ch=getchar()) != '\n'){        len ++;        int id = ch - 'a';        if(nextt[p][id] == -1){            q = ++ totTrie;            v[q] = now;            memset(nextt[q],-1,sizeof(nextt[q]));            nextt[p][id] = q;            p = q;           }    else {            p = nextt[p][id];            if(v[p] != 0 ) T[now] = Insert(T[now], v[p], -1);            v[p] = now;        }    }    T[now] = Insert(T[now], now, len);}int main() {    //freopen("data.in","r",stdin);    while( ~scanf("%d",&N)){        getchar();        memset(v,0,sizeof(v));        memset(nextt[0], -1 ,sizeof(nextt[0]));        now = totTrie = tot = 0;        T[0] = build(1, N);        for(int i = 1; i <= N; i++){            ++ now;            T[i] = T[i-1];            InsertTrie();        }        scanf("%d",&Q);        int x,y,z;        z=0;        while(Q--){            scanf("%d%d",&x,&y);            int L,R;            L=min( (z+x)%N  , (z+y)%N ) +1;            R=max( (z+x)%N, (z+y)%N) +1;            z=Query(T[R], L, 1, N);            printf("%d\n",z);        }    }    return 0;}
原创粉丝点击