[CodeForces 697F][CodeForces 696D] Legen...(AC自动机+矩阵加速)

文章目录

题目

time limit per test: 6 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output
Description
Barney was hanging out with Nora for a while and now he thinks he may have feelings for her. Barney wants to send her a cheesy text message and wants to make her as happy as possible.
题目插图
Initially, happiness level of Nora is 0 0 0. Nora loves some pickup lines like “I’m falling for you” and stuff. Totally, she knows n n n pickup lines, each consisting only of lowercase English letters, also some of them may be equal (in writing, but different in pronouncing or meaning though). Every time Nora sees i i i-th pickup line as a consecutive subsequence of Barney’s text message her happiness level increases by a i a_i ai. These substrings may overlap, for example, Nora will see the pickup line aa twice and the pickup line ab once in text message aaab.

Due to texting app limits, Barney’s text may have up to l characters.

Barney asked you to help him make Nora as much happy as possible, it’s gonna be legen…

Input
The first line of input contains two integers n n n and l l l ( 1   ≤   n   ≤   200 1 ≤ n ≤ 200 1n200,  1   ≤   l   ≤   1 0 14 1 ≤ l ≤ 10^{14} 1l1014) — the number of pickup lines and the maximum length of Barney’s text.

The second line contains n n n integers a 1 ,   a 2 ,   ⋯   ,   a n a_1, a_2, \cdots, a_n a1,a2,,an ( 1   ≤   a i   ≤   100 1 ≤ a_i ≤ 100 1ai100), meaning that Nora’s happiness level increases by a i a_i ai after every time seeing i i i-th pickup line.

The next n n n lines contain the pickup lines. i i i-th of them contains a single string s i s_i si consisting of only English lowercase letter. Summary length of all pickup lines does not exceed 200 200 200.

All strings are not empty.

Output
Print the only integer — the maximum possible value of Nora’s happiness level after reading Barney’s text.

Examples
input

3 6
3 2 1
heart
earth
art

output

6

input

3 6
3 2 8
heart
earth
art

output

16

Note
An optimal answer for the first sample case is hearth containing each pickup line exactly once.
An optimal answer for the second sample case is artart.

分析

不管怎么说,这是我第一道矩阵加速的题(第二道AC自动机),写了2个小时,在CSP前两天的时候调了一下午。
耶
一道题,会越做越觉得水,当我画了4+小时把它gan完,觉得它已经变成汪洋大海了。


构造字符串的套路,就是建踹图。大家可以先看一下这道题(的题解):POJ2778 DNA Sequence

踹(Trie)图是什么,就是你的AC自动机搞完fail指针并且传递好信息过后的图(fail指针不是边)。这个图有几个美妙的性质:

  • 踹图强联通;
  • 你从根开始随便走,可以得到一个字符串 s s s,并且 s s s想要多长有多长;
  • 由于模式串的信息是存在尾字符对应的结点上的且你搞fail的时候进行了传递,那么 s s s中所有结点的信息就是 s s s所有模式子串的信息。

现在我们就可以在本题的踹图上DP: d p [ i ] dp[i] dp[i]表示由 i i i个字符组成的字符串,得到的最大快乐值,那么 d p [ i ] = max ⁡ j ∈ f a i { d p [ j ] + n u m j } dp[i]=\max\limits_{j\in fa_i}\{dp[j]+num_j\} dp[i]=jfaimax{dp[j]+numj}注意踹图不是树。
n u m j num_j numj是这个字符串的所有后缀的快乐值之和,可以在GetFail里面再DP一下(把自己的快乐值加上自己的fail点的快乐值即可,这也是一个按Bfs顺序的DP)。

于是,在踹图上做一个 O ( n ⋅ l ) O(n\cdot l) O(nl)(?)的DP,反正把太湖之光借给你都跑不过。


于是,我现在才发现了一个神奇的结论:一个 n n n个结点组成的图的邻接矩阵 G G G m m m次幂 G m G^m Gm中, G m [ i ] [ j ] G^m[i][j] Gm[i][j]表示 i i i通过 m m m条边到 j j j的方案数。

证明
G m + 1 = G m × G G^{m+1}=G^m\times G Gm+1=Gm×G,根据定义: G m + 1 [ i ] [ j ] = ∑ k = 1 n ( G m [ i ] [ k ] ⋅ G [ k ] [ j ] ) G^{m+1}[i][j]=\sum\limits_{k=1}^{n}\left(G^m[i][k]\cdot G[k][j]\right) Gm+1[i][j]=k=1n(Gm[i][k]G[k][j])相当于枚举倒数第二个点,进行一个Floyd式的DP,得证。


把它类比到这个题里面,做出踹图的邻接矩阵(有边权,是边权快乐值),由于这个 d p dp dp是取 max ⁡ \max max,所以,上面的式子要变成: G m + 1 [ i ] [ j ] = max ⁡ k = 1 n { G m [ i ] [ k ] + G [ k ] [ j ] } G^{m+1}[i][j]=\max\limits_{k=1}^{n}\left\{G^m[i][k]+G[k][j]\right\} Gm+1[i][j]=k=1maxn{Gm[i][k]+G[k][j]} k k k还得满足: m m m步以内 i i i能到达 k k k,一步以内 k k k能到达 j j j
这玩意有结合律(感性理解海星),所以可以矩阵快速幂。


讲完了,还有点细节,-1表示不连通而不能用0表示,因为有的联通但快乐值为0。还有GetFail的写法什么的。还有中途纠结了几千万遍根是 1 1 1号店还是 0 0 0号点,最后选择了像 1 1 1号店屈服。


我不会告诉你我的海口复习法有多好用。
海口复习法

代码

#include <cstdio>
#include <cstring>
#include <queue>

typedef long long LL;

struct Matrix {
    static const int MAXN = 200;

    int N, M;
    LL A[MAXN +5][MAXN + 5];

    Matrix() {
        N = M = 0;
        memset(A, 0, sizeof A);
    }

    Matrix(int row, int col, int sign = 0): N(row), M(col) {
        memset(A, 0, sizeof A);
        for (int i = 1; i <= row; i++)
            for (int j = 1; j <= col; j++)
                A[i][j] = sign;
    }

    LL* operator [] (const int &i) {
        return A[i];
    }

    Matrix operator * (Matrix &B) {
        Matrix ret(N, B.M, -1);
        for (int i = 1; i <= N; i++)
            for (int j = 1; j <= M; j++)
                for (int k = 1; k <= B.N; k++)
                    if (A[i][k] != -1 && B[k][j] != -1)
                        ret[i][j] = std::max(ret[i][j], A[i][k] + B[k][j]);
        return ret;
    }

    void Output() {
        puts("");
        puts("-- Debug --");
        printf("\t");
        for (int i = 1; i <= N; i++)
            printf("(%d)\t", i);
        for (int i = 1; i <= N; i++){
            printf("\n(%d)\t", i);
            for (int j = 1; j <= M; j++)
                printf("%lld\t", A[i][j]);
        }
        puts("");
    }

    Matrix operator ^ (LL y) {
        Matrix ret(N, N, -1), base = *this;
        ret[1][1] = 0;
        while (y) {
            if (y & 1)
                ret = ret * base;
//            printf("< ret >"), ret.Output();
            y >>= 1;
            base = base * base;
//            printf("< base >"), base.Output();
        }
        return ret;
    }
};

struct AC_Automaton {
    static const int MAXC = 26;
    static const int MAXL = 200;

    struct Node {
        char dbg;
        int num;
        int ch[MAXC +5], fal;
    }T[MAXL + 5];
    int NodeCnt;

    AC_Automaton() {
        NodeCnt = 1, T[1].dbg = '$';
        memset(T, 0, sizeof T);
    }

    int NewNode(const char &cur) {
        T[++NodeCnt].dbg = cur;
        return NodeCnt;
    }

    void Insert(const char *str, const int &hps) {
        int len = strlen(str), u = 1;
        for (int i = 0; i < len; i++) {
            int id = str[i] - 'a';
            if (!T[u].ch[id])
                T[u].ch[id] = NewNode(str[i]);
            u = T[u].ch[id];
        }
        T[u].num += hps;
    }

    void GetFail() {
        std::queue<int> Q;
        for (int i = 1; i <= NodeCnt; i++)
            T[i].fal = 1;
        for (int i = 0; i < MAXC; i++)
            if (T[1].ch[i])
                Q.push(T[1].ch[i]);
            else
                T[1].ch[i] = 1;
        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            T[u].num += T[T[u].fal].num;
            for (int i = 0; i < MAXC; i++) {
                int v = T[u].ch[i], nxt = T[T[u].fal].ch[i];
                if (v) {
                    Q.push(v);
                    T[v].fal = nxt;
                }
                else
                    T[u].ch[i] = nxt;
            }
        }
    }
};

const int MAXN = 200;

int N; LL L;
int A[MAXN + 5];
char tmp[MAXN + 5];


int main() {
    scanf("%d%lld", &N, &L);
    for (int i = 1; i <= N; i++)
        scanf("%d", &A[i]);
    AC_Automaton Trie;
    for (int i = 1; i <= N; i++) {
        scanf("%s", tmp);
        Trie.Insert(tmp, A[i]);
    }
    Trie.GetFail();
//    for (int i = 1; i <= Trie.NodeCnt; i++)
//        printf("%c %d Fail = %d\n", Trie.T[i].dbg, Trie.T[i].num, Trie.T[i].fal);
//    puts("");
    Matrix dp(Trie.NodeCnt, Trie.NodeCnt, -1);
    for (int i = 1; i <= Trie.NodeCnt; i++)
        for (int j = 0; j < AC_Automaton::MAXC; j++) {
            int v = Trie.T[i].ch[j];
            dp[i][v] = Trie.T[v].num;
        }
//    dp.Output();
    dp = dp ^ L;
//    dp.Output();
    LL Ans = 0;
    for (int j = 1; j <= dp.M; j++)
        Ans = std::max(Ans, dp[1][j]);
    printf("%lld", Ans);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值