2016hdu多校賽第5場(hdu5790) 主席樹(Persistent Segment Tree)

題意

給你N個字符串,問你第l個到第r個字符串中有多少個不同前綴。強制在線做,所以沒法用樹狀數組來做。

主席樹

也就是Persistent Segment Tree ,可持久化線段樹。

一般來講線段樹更新之後不會使用歷史版本的線段樹的信息,但是有些問題裏面需要。

可持久化線段樹的做法是,新建一logn個節點,相當於一條鏈,線段樹原本更新時是將這條鏈上的值更新,而主席樹是新建一條鏈,其他部分連接原始的線段樹,這樣既完成了更新操作,又能保留歷史版本。

做法

這個做法同在線查詢一個區間內不同數的個數,T[i]這個樹維護從i開始[i,r]不同數的個數,用一個數組bel維護出現那個前綴最後的位置,從T[i]轉移到T[i-1]就是將當前所有前綴在後面出現過的話就消除影響,並且在當前位置加上當前字符串個數(也就是前綴個數)

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<iostream>

using namespace std;

namespace Trie {
    const int SIZE = 100005;
    int node[SIZE][26];
    int tot, bel[SIZE];
    void Insert(string& str) {
        int cur = 0;
        for(int i = 0; i < str.size(); i++) {
            int p = str[i] - 'a';
            if(node[cur][p] == 0) {
                tot++;
                node[cur][p] = tot; //指向新節點
                memset(node[tot], 0, sizeof(node[tot]));
            }
            cur = node[cur][p];
            bel[cur] = 0;
        }
    }
    void init() {
        tot = 0;
        memset(node[0], 0, sizeof(node[0]));
    }
    // cur <- node[cur][p]
}

namespace PST {

    const int MAXN = 100005;
    const int M = MAXN * 40;
    int tot;
    int n;
    int T[MAXN],lson[M],rson[M],c[M];
    void init(int _n) {
        tot = 0;
        n = _n;
    }
    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 update(int root,int pos,int val)
    {
        int newroot = tot++, tmp = newroot;
        c[newroot] = c[root] + val;
        int l = 1, r = n;
        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 pos)
    {
        int ret = 0;
        int l = 1, r = n;
        while(pos < r)
        {
            int mid = (l+r)>>1;
            if(pos <= mid)
            {
                r = mid;
                root = lson[root];
            }
            else
            {
                ret += c[lson[root]];
                root = rson[root];
                l = mid+1;
            }
        }
        return ret + c[root];
    }
}

string s[PST::MAXN];

int main() {
    int N;
    while(~scanf("%d",&N)) {
        PST::init(N);
        Trie::init();
        for(int i = 1; i <= N; i++) {
            cin >> s[i];
            Trie::Insert(s[i]);
        }
        PST::T[N+1] = PST::build(1, N);
        for(int i = N; i >= 1; i--) {
            int cur = 0;
            PST::T[i] = PST::T[i+1];
            for(int j = 0; j < s[i].size(); j++) {
                int p = s[i][j] - 'a';
                cur = Trie::node[cur][p];
                if(Trie::bel[cur]) {
                    //消除前面出現過的前綴的影響
                    PST::T[i] = PST::update(PST::T[i], Trie::bel[cur], -1);
                }
                Trie::bel[cur] = i;//通過字典樹分配ID,記錄這個前綴出現的最後一個位置
            }

            PST::T[i] = PST::update(PST::T[i],i,s[i].size());
        }
        int Q;
        scanf("%d",&Q);
        int Z = 0;
        while(Q--) {
            int l, r;
            scanf("%d%d",&l,&r);
            l += Z; l %= N;
            r += Z; r %= N;
            if(l > r) swap(l, r);
            Z = PST::query(PST::T[l+1],r+1);
            printf("%d\n",Z);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章