zoukankan      html  css  js  c++  java
  • Namomo Cockfight Round 5-D String (01字典树,tire)

    Namomo Cockfight Round 5-D String (01字典树,tire)

    题面:

    思路:

    很显然是一个字典树的题,且只有0,1字符。

    我们对于字典树每一个节点的开以下信息:

    (tree[i][0 or 1]) 代表第i 个节点的0 or 1儿子节点的编号。

    (e[i])代表在第(mathit i) 个节点结束的所有字符串的总价值。

    (pass[i])代表经过第(mathit i) 个节点的所有字符串的总价值。

    (flagg[i]) 代表是否有在第(mathit i) 个节点结束的字符串,(因为本题目所有字符串的价值大于( ext 0) ,所以这个数组其实可以用([e[i]>0]) 代替,我就直接偷懒不改了。)

    插入,查询,删除都是字典树的常规,我这里就不做详述了,

    重点讲一下第四个操作,即更换字符串前缀。

    我们先找到(rt2)代表以字符串(mathit s) 为前缀的子树根节点编号。

    (rt1)代表以字符串(mathit t) 为前缀的子树根节点编号。

    显然(pass[rt2]) 是所有以字符串(mathit s) 为前缀的字符串价值总和。

    我们应该对(root->rt2)路径中每一个节点(mathit i)(pass[i]) 减掉(pass[rt2])。(root是总树根)

    也要对(root->rt1)路径中每一个节点(mathit j)(pass[j]) 加上(pass[rt2])

    然后将(rt2) 为根的子树合并到(rt1)为根的子树中。

    合并过程如下:

    (rt2)为根的子树叫右树,(rt1)为根的左树

    对于每一个递归到的节点,将其有右树中的节点信息((pass[j],e[j],flagg[j])等等)加到左树中。

    如果左树中的一个节点没有(0/1)儿子,而右树对应对着的节点有该儿子,那么直接将该儿子与右树断开,连在左数上对应位置即可。

    可以看下下图理解一下:

    很多人认为该部分操作是(O(n))的,实则不是,

    考虑均摊时间,每一次你合并了(num)个节点,时间复杂度是(O(num))的,但是整个树形中节点就会减少(num)个,即每一个节点只会被删除一次,所以读入中的每一个字符的贡献都是一个常数。

    这题的总时间复杂度就为(O(sum |str|))

    官方题解为:

    本题在trie上维护包含某个前缀的串的值的和,直接暴力即可。下面介绍如何分析均摊复杂度,即证明每个输入的字符只会产生常数的运行时间。插入字符串时,从trie的根开始,每个字符会往下走一步,如果没有对应的点会新建一个点。走到最下面把维护的值加上这次插入的串的值即可。这样每个字符只消耗常数时间(走一步和新建一个点)。删除类似,走到字符串对应的点后,用该点维护的值减掉它两个子树维护的值,就可以得到所有字符串s的值的和,称为d。再从这个点走回根,沿途将维护的值减少d即可。询问也类似,走到对应的点输出维护的值。最后一种修改操作,由于s和t公共前缀为空,所以代表sx的串在trie中对应的子树和代表tx的串对应的子树无交。我们找到两棵子树的根(称为s和t),递归的合并他们。首先把s维护的值加在t维护的值上,t作为合并后的根,s可以删除。对于s和t各自的左儿子(字符为0的儿子),如果至少一个儿子不存在,把存在的那个儿子作为合并后的左儿子即可。如果两个左儿子都存在,递归合并这两个左儿子。右儿子同理。这样,每次要么递归不继续进行,要么在递归的同时trie里面点的数量会减一(两个根合并后只剩一个)。由于trie里每个点都是某个输入的字符创建的,所以每次递归的复杂度平摊在每个输入的字符上只有常数。

    代码:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <bits/stdc++.h>
    #define ALL(x) (x).begin(), (x).end()
    #define sz(a) int(a.size())
    #define rep(i,x,n) for(int i=x;i<n;i++)
    #define repd(i,x,n) for(int i=x;i<=n;i++)
    #define pii pair<int,int>
    #define pll pair<long long ,long long>
    #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
    #define MS0(X) memset((X), 0, sizeof((X)))
    #define MSC0(X) memset((X), '', sizeof((X)))
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define eps 1e-6
    #define chu(x)  if(DEBUG_Switch) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
    #define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
    #define du2(a,b) scanf("%d %d",&(a),&(b))
    #define du1(a) scanf("%d",&(a));
    using namespace std;
    typedef long long ll;
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
    ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
    ll poww(ll a, ll b) { if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a ;} a = a * a ; b >>= 1;} return ans;}
    void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("
    ");}}}
    void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("
    ");}}}
    inline long long readll() {long long tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
    inline int readint() {int tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') fh = -1; c = getchar();} while (c >= '0' && c <= '9') tmp = tmp * 10 + c - 48, c = getchar(); return tmp * fh;}
    void pvarr_int(int *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%d%c", arr[i], i == n ? '
    ' : ' ');}}
    void pvarr_LL(ll *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%lld%c", arr[i], i == n ? '
    ' : ' ');}}
    // const int maxn = 1000010;
    const int inf = 0x3f3f3f3f;
    /*** TEMPLATE CODE * * STARTS HERE ***/
    #define DEBUG_Switch 0
    const int maxn = 2e6 + 5; //如果是64MB可以开到2e6+5,尽量开大
    int tree[maxn][2];//tree[i][j]表示节点i的第j个儿子的节点编号
    ll e[maxn], pass[maxn];
    bool flagg[maxn];//表示以该节点结尾是一个单词
    int tot;//总节点数
    void insert_(char *str, ll val)
    {
        int len = strlen(str);
        int root = 0;
        for (int i = 0; i < len; i++)
        {
            int id = str[i] - '0';
            if (!tree[root][id])
                tree[root][id] = ++tot;
            root = tree[root][id];
            pass[root] += val;
        }
        e[root] += val;
        flagg[root] = true;
    }
    ll search_(char *str)// 查询以str为前缀的字符串数量(val_sum)。
    {
        int len = strlen(str);
        int root = 0;
        for (int i = 0; i < len; i++)
        {
            int id = str[i] - '0';
            if (!tree[root][id])
                return 0;
            root = tree[root][id];
        }
        return pass[root];
    }
    bool del(char *str)
    {
        int len = strlen(str);
        int root = 0;
        for (int i = 0; i < len; i++)
        {
            int id = str[i] - '0';
            if (!tree[root][id])
                return 0;
            root = tree[root][id];
        }
        if (flagg[root] == 0 || pass[root] == 0)
            return 0;
        ll val = e[root];
        root = 0;
        for (int i = 0; i < len; i++)
        {
            int id = str[i] - '0';
            root = tree[root][id];
            pass[root] -= val;
        }
        e[root] = 0;
        flagg[root] = 0;
        return 1;
    }
    void init()//最后清空,节省时间
    {
        for (int i = 0; i <= tot; i++)
        {
            flagg[i] = false;
            pass[i] = 0;
            e[i] = 0;
            for (int j = 0; j < 2; j++)
                tree[i][j] = 0;
        }
        tot = 0; //RE有可能是这里的问题
    }
    int search_root(char *str, ll &val) // 查询以str为前缀的树根。,val=以str为前缀的串总值。
    {
        int len = strlen(str);
        int root = 0;
        for (int i = 0; i < len; i++)
        {
            int id = str[i] - '0';
            if (!tree[root][id])
                return 0;
            root = tree[root][id];
        }
        val = pass[root];
        if (val == 0)// 已经被删除
            return 0;
        return root;
    }
    void break_root(int root)// 断掉该根的子树信息。
    {
        tree[root][0] = tree[root][1] = 0;
        flagg[root] = 0;
        pass[root] = e[root] = 0;
    }
    int get_root(char *str, ll val) // 返回以str为前缀的字符串子树根,并将val值加到树根到子树根这个路径中
    {
        int len = strlen(str);
        int root = 0;
        for (int i = 0; i < len; i++)
        {
            int id = str[i] - '0';
            if (!tree[root][id])
                tree[root][id] = ++tot;
            root = tree[root][id];
            pass[root] += val;
        }
        return root;
    }
    void merge__(int rt1, int rt2)// 合并 分别以rt1,rt2为根的子树(将rt2的信息挂在rt1上)
    {
        pass[rt1] += pass[rt2];
        e[rt1] += e[rt2];
        flagg[rt1] += flagg[rt2];
    
        pass[rt2] = e[rt2] = flagg[rt2] = 0;
    
        repd(i, 0, 1)
        {
            if (tree[rt2][i] != 0)
            {
                if (tree[rt1][i] == 0)
                {
                    tree[rt1][i] = tree[rt2][i];
                } else
                {
                    merge__(tree[rt1][i], tree[rt2][i]);
                }
            }
        }
    }
    void del_pre(char *str, ll &val) // 删除以str为前缀的字符串的值。
    {
        int len = strlen(str);
        int root = 0;
        for (int i = 0; i < len; i++)
        {
            int id = str[i] - '0';
            root = tree[root][id];
            pass[root] -= val;
        }
    }
    char s[maxn];
    char ct[maxn];
    int main()
    {
    #if DEBUG_Switch
        freopen("C:\code\input.txt", "r", stdin);
    #endif
        //freopen("C:\code\output.txt","w",stdout);
        int t;
        t = readint();
        char op[10];
        for (int cas = 1; cas <= t; ++cas)
        {
            printf("Case %d
    ", cas );
            int q = readint();
            // i str val -> insert a string with it's value
            // d str     -> delete all str strings  
            // q str     -> get the sum of the values of all strings prefixed by str
            // u s t    对于所有形式为 sx 的字符串, 把他们修改成 tx, 其中 x 是任意字符串,可以为空。
            //          如果不存在这样的字符串,输出 "Not Exist"。数据保证 s 和 t 的最长公共前缀为空。
            while (q--)
            {
                scanf("%s", op);
                if (op[0] == 'I')
                {
                    scanf("%s", s);
                    ll val;
                    scanf("%lld", &val);
                    insert_(s, val);
                } else if (op[0] == 'D')
                {
                    scanf("%s", s);
                    bool res = del(s);
                    if (res == 0)
                    {
                        printf("Not Exist
    ");
                    }
                } else if (op[0] == 'Q')
                {
                    scanf("%s", s);
                    ll res = search_(s);
                    printf("%lld
    ", res );
                } else
                {
                    scanf("%s", s);
                    scanf("%s", ct);
                    ll w;
                    int rt2 = search_root(s, w);
                    if (rt2 == 0)
                    {
                        printf("Not Exist
    ");
                    } else
                    {
                        int rt1 = get_root(ct, w);
                        pass[rt1] -= pass[rt2];// pass[rt1] 在get_root,merge_被加了2次,这里需要减去一次。
                        merge__(rt1, rt2);
                        del_pre(s, w);
                        break_root(rt2);
                    }
                }
            }
            init();
        }
        return 0;
    }
    
    
    本博客为本人原创,如需转载,请必须声明博客的源地址。 本人博客地址为:www.cnblogs.com/qieqiemin/ 希望所写的文章对您有帮助。
  • 相关阅读:
    入门菜鸟
    FZU 1202
    XMU 1246
    Codeforces 294E Shaass the Great 树形dp
    Codeforces 773D Perishable Roads 最短路 (看题解)
    Codeforces 814E An unavoidable detour for home dp
    Codeforces 567E President and Roads 最短路 + tarjan求桥
    Codeforces 567F Mausoleum dp
    Codeforces 908G New Year and Original Order 数位dp
    Codeforces 813D Two Melodies dp
  • 原文地址:https://www.cnblogs.com/qieqiemin/p/13518947.html
Copyright © 2011-2022 走看看