• hdu4117


    题意:给出一串单词,每个有一个权值。顺序不变的情况下,删掉一些,使得相邻两单词,前一个是后一个的子串。同时要求使得剩余单词权值和最大。求最大是多少。

    分析:

    AC自动机+线段树+DP。

    这是一个比较复杂的题目,我们分步来讲解。

    第一部分,动态规划。

    用f[i]表示从第1个单词,到第i个单词,所有剩余单词中包含第i个的情况中最大权值和是多少。

    f[i]=max(f[v]+weight[i]),要求第v个单词是第i个单词的子串且v<i。

    第二部分,利用AC自动机求所有子串。

    fail指针就是找后缀,一个串的子串就是某前缀的后缀。因此我们在建立好自动机之后将一个串重新从root节点开始走,

    第三部分,fail树的建立。

    我们不是真正的通过fail指针找某串的子串,而是通过fail反向指针找所有以该串为后缀的串。

    由于每个节点只有一个fail指针,因此我们可以从root开始利用fail指针的逆指针建立一个fail树。

    这个树的意义是,其中每个节点的祖先都是它的后缀。每个节点的子孙都是在该节点的串的前面加入了不同的内容产生的。

    我们给fail树中的每个节点v附加一个额外的值f[v](就是第一部分中说的),f[v]的值更新之后会影响到fail树中该串对应节点的子孙的f值。

    当我们要计算f[i]时,要分别观察a的所有前缀所在fail树中的值。f[i]=max(f[v]+weight[i]),v是i的所有前缀在fail树中的所有祖先。

    现在问题变成了一个,动态改变树中点的权值,并询问某点的祖先中最大值的问题。可以用线段树来解决。

    先对fail树进行时间戳标记,这样每个子树对应一个区间,然后每个权值的改变都更新线段书上的一个区间(fail树中的一个子树)即可。

    询问时分别询问每个前缀的f[i]取最大即可。

    树的时间戳标记模板如下:

    void dfs(int u, int parent)
    {
        dfn[u][0] = ++dfn_cnt;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if (v != parent)
            {
                dfs(v, u);
            }
        }
        dfn[u][1] = dfn_cnt;
    }
    View Code

    线段树框架模板如下:

    struct SegmentTree
    {
    
        struct Node
        {
            int l, r;
            Node *pleft, *pright;
            //add the needed variable
        }tree[MAX_INTERVAL *4];
    
        int node_cnt;
    
        void init()
        {
            node_cnt = 0;
        }
    
        Node* new_node()
        {
            node_cnt++;
            return tree + node_cnt;
        }
    
        void build_tree(Node *proot, int s, int e)
        {
            proot->l = s;
            proot->r = e;
            //init the variables
            if (s == e)
            {
                proot->pleft = proot->pright = NULL;
                return;
            }
            int mid = (s + e) / 2;
            build_tree(proot->pleft = new_node(), s, mid);
            build_tree(proot->pright = new_node(), mid + 1, e);
        }
    
        void pull_up(Node *proot)
        {
                //do something
        }
    
        void push_down(Node *proot)
        {
                //do something
        }
    
        void update(Node *proot, int start, int end, int value)
        {
            if (start > proot->r || end < proot->l)
                return;
            start = max(start, proot->l);
            end = min(end, proot->r);
            if (start == proot->l && end == proot->r)
            {
                //do something
                return;
            }
            push_down(proot);
            update(proot->pleft, start, end, value);
            update(proot->pright, start, end, value);
            pull_up(proot);
        }
    
        int query(Node *proot, int start, int end)
        {
            int ret = proot->value;
            if (start > proot->r || end < proot->l)
                return 0;
            start = max(start, proot->l);
            end = min(end, proot->r);
            if (start == proot->l && end == proot->r)
            {
                //do something
            }
            push_down(proot);
            ret = max(ret, query(proot->pleft, start, end));
            ret = max(ret, query(proot->pright, start, end));
            pull_up(proot);
            return ret;
        }
    };
    View Code

    代码如下:

    #pragma comment(linker, "/STACK:1024000000,1024000000")
    #include <cstdio>
    #include <queue>
    #include <cstring>
    using namespace std;
    
    #define D(x)
    
    const int MAX_CHILD_NUM = 26;
    const int MAX_NODE_NUM = 3 * (int)1e5 + 10;
    const int MAX_LEN = 3 * (int)1e5 + 10;
    const int MAX_N = 2 * (int)1e4 + 10;
    
    #define MAX_EDGE_NUM MAX_NODE_NUM * 2
    
    struct Edge
    {
        int v, next;
        Edge()
        {}
        Edge(int v, int next):v(v), next(next)
        {}
    } edge[MAX_EDGE_NUM];
    
    int head[MAX_NODE_NUM];
    int edge_cnt;
    
    void init_edge()
    {
        memset(head, -1, sizeof(head));
        edge_cnt = 0;
    }
    
    void add_edge(int u, int v)
    {
        edge[edge_cnt] = Edge(v, head[u]);
        head[u] = edge_cnt++;
    }
    
    struct Trie
    {
        int next[MAX_NODE_NUM][MAX_CHILD_NUM];
        int fail[MAX_NODE_NUM];
        int count[MAX_NODE_NUM];
        int node_cnt;
        int root;
        bool vis[MAX_NODE_NUM]; //set it to false
    
        void init()
        {
            node_cnt = 0;
            root = newnode();
        }
    
        int newnode()
        {
            for (int i = 0; i < MAX_CHILD_NUM; i++)
                next[node_cnt][i] = -1;
            count[node_cnt++] = 0;
            return node_cnt - 1;
        }
    
        int get_id(char a)
        {
            return a - 'a';
        }
    
        void insert(char buf[], int index)
        {
            int now = root;
            for (int i = 0; buf[i]; i++)
            {
                int id = get_id(buf[i]);
                if (next[now][id] == -1)
                    next[now][id] = newnode();
                now = next[now][id];
            }
            count[now] = index;
        }
    
        void build()
        {
            queue<int>Q;
            fail[root] = root;
            for (int i = 0; i < MAX_CHILD_NUM; i++)
                if (next[root][i] == -1)
                    next[root][i] = root;
                else
                {
                    fail[next[root][i]] = root;
                    Q.push(next[root][i]);
                }
            while (!Q.empty())
            {
                int now = Q.front();
                Q.pop();
                for (int i = 0; i < MAX_CHILD_NUM; i++)
                    if (next[now][i] == -1)
                        next[now][i] = next[fail[now]][i];
                    else
                    {
                        fail[next[now][i]]=next[fail[now]][i];
                        Q.push(next[now][i]);
                    }
            }
        }
    
        void debug()
        {
            for(int i = 0;i < node_cnt;i++)
            {
                printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],count[i]);
                for(int j = 0;j < MAX_CHILD_NUM;j++)
                    printf("%2d",next[i][j]);
                printf("]
    ");
            }
        }
    
    
        void build_fail_tree()
        {
            init_edge();
            for (int i = 1; i < node_cnt; i++)
            {
                add_edge(i, fail[i]);
                add_edge(fail[i], i);
            }
        }
    
    }ac;
    
    const int MAX_INTERVAL = MAX_LEN;
    
    struct SegmentTree
    {
    
        struct Node
        {
            int l, r;
            Node *pleft, *pright;
            int value;
        }tree[MAX_INTERVAL *4];
    
        int node_cnt;
    
        void init()
        {
            node_cnt = 0;
        }
    
        Node* new_node()
        {
            node_cnt++;
            return tree + node_cnt;
        }
    
        void build_tree(Node *proot, int s, int e)
        {
            proot->l = s;
            proot->r = e;
            proot->value = 0;
            if (s == e)
            {
                proot->pleft = proot->pright = NULL;
                return;
            }
            int mid = (s + e) / 2;
            build_tree(proot->pleft = new_node(), s, mid);
            build_tree(proot->pright = new_node(), mid + 1, e);
        }
    
        void pull_up(Node *proot)
        {
        }
    
        void push_down(Node *proot)
        {
        }
    
        void update(Node *proot, int start, int end, int value)
        {
            if (start > proot->r || end < proot->l)
                return;
            start = max(start, proot->l);
            end = min(end, proot->r);
            if (start == proot->l && end == proot->r)
            {
                proot->value = max(proot->value, value);
                return;
            }
            push_down(proot);
            update(proot->pleft, start, end, value);
            update(proot->pright, start, end, value);
            pull_up(proot);
        }
    
        int query(Node *proot, int start, int end)
        {
            int ret = proot->value;
            if (start > proot->r || end < proot->l)
                return 0;
            start = max(start, proot->l);
            end = min(end, proot->r);
            if (start == proot->l && end == proot->r)
            {
                return ret;
            }
            push_down(proot);
            ret = max(ret, query(proot->pleft, start, end));
            ret = max(ret, query(proot->pright, start, end));
            pull_up(proot);
            return ret;
        }
    }tree;
    
    char st[MAX_LEN];
    int pos[MAX_N];
    int dfn[MAX_LEN][2];
    int dfn_cnt;
    int n;
    int weight[MAX_N];
    
    void dfs(int u, int parent)
    {
        dfn[u][0] = ++dfn_cnt;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if (v != parent)
            {
                dfs(v, u);
            }
        }
        dfn[u][1] = dfn_cnt;
    }
    
    int work()
    {
        int ret = 0;
        tree.init();
        tree.build_tree(tree.tree, 0, dfn_cnt);
        for (int i = 0; i < n; i++)
        {
            int u = ac.root;
            int temp = 0;
            for (int j = pos[i]; j < pos[i + 1]; j++)
            {
                u = ac.next[u][ac.get_id(st[j])];
                temp = max(temp, tree.query(tree.tree, dfn[u][0], dfn[u][0]) + weight[i]);
            }
            tree.update(tree.tree, dfn[u][0], dfn[u][1], temp);
            ret = max(ret, temp);
        }
        return ret;
    }
    
    void input()
    {
        scanf("%d", &n);
        int temp = 0;
        for (int i = 0; i < n; i++)
        {
            scanf("%s%d", st + temp, &weight[i]);
            pos[i] = temp;
            ac.insert(st + temp, i);
            int len = strlen(st + temp);
            temp += len;
        }
        pos[n] = temp;
    }
    
    int main()
    {
        int t;
        scanf("%d", &t);
        for (int i = 1; i <= t; i++)
        {
            ac.init();
            input();
            ac.build();
            ac.build_fail_tree();
            dfn_cnt = 0;
            dfs(0, -1);
            printf("Case #%d: %d
    ", i, work());
        }
        return 0;
    }
    View Code
  • 相关阅读:
    C#博客随笔之四:使用C#模拟办公网登录HttpClient的使用
    C#博客随笔之三:Linq in C#
    C#博客随笔之二:wp开发之弹出对话框
    C#博客随笔之一:使用C#的第一个WP程序
    Fedora15命令速查手册
    乐观是一种智慧
    完全教程 Aircrackng破解WEP、WPAPSK加密利器
    FreeBSD常用命令大全
    Linux 网络管理员指南——前言
    API
  • 原文地址:https://www.cnblogs.com/rainydays/p/4351373.html
Copyright © 2020-2023  润新知