[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 00. Nora loves some pickup lines like “I’m falling for you” and stuff. Totally, she knows nn 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 ii-th pickup line as a consecutive subsequence of Barney’s text message her happiness level increases by aia_i. 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 nn and ll (1n2001 ≤ n ≤ 200, 1l10141 ≤ l ≤ 10^{14}) — the number of pickup lines and the maximum length of Barney’s text.

The second line contains nn integers a1,a2,,ana_1, a_2, \cdots, a_n (1ai1001 ≤ a_i ≤ 100), meaning that Nora’s happiness level increases by aia_i after every time seeing ii-th pickup line.

The next nn lines contain the pickup lines. ii-th of them contains a single string sis_i consisting of only English lowercase letter. Summary length of all pickup lines does not exceed 200200.

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指針不是邊)。這個圖有幾個美妙的性質:

  • 踹圖強聯通;
  • 你從根開始隨便走,可以得到一個字符串ss,並且ss想要多長有多長;
  • 由於模式串的信息是存在尾字符對應的結點上的且你搞fail的時候進行了傳遞,那麼ss中所有結點的信息就是ss所有模式子串的信息。

現在我們就可以在本題的踹圖上DP:dp[i]dp[i]表示由ii個字符組成的字符串,得到的最大快樂值,那麼dp[i]=maxjfai{dp[j]+numj}dp[i]=\max\limits_{j\in fa_i}\{dp[j]+num_j\}注意踹圖不是樹。
numjnum_j是這個字符串的所有後綴的快樂值之和,可以在GetFail裏面再DP一下(把自己的快樂值加上自己的fail點的快樂值即可,這也是一個按Bfs順序的DP)。

於是,在踹圖上做一個O(nl)O(n\cdot l)(?)的DP,反正把太湖之光借給你都跑不過。


於是,我現在才發現了一個神奇的結論:一個nn個結點組成的圖的鄰接矩陣GGmm次冪GmG^m中,Gm[i][j]G^m[i][j]表示ii通過mm條邊到jj的方案數。

證明
Gm+1=Gm×GG^{m+1}=G^m\times G,根據定義:Gm+1[i][j]=k=1n(Gm[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)相當於枚舉倒數第二個點,進行一個Floyd式的DP,得證。


把它類比到這個題裏面,做出踹圖的鄰接矩陣(有邊權,是邊權快樂值),由於這個dpdp是取max\max,所以,上面的式子要變成:Gm+1[i][j]=maxk=1n{Gm[i][k]+G[k][j]}G^{m+1}[i][j]=\max\limits_{k=1}^{n}\left\{G^m[i][k]+G[k][j]\right\}kk還得滿足:mm步以內ii能到達kk,一步以內kk能到達jj
這玩意有結合律(感性理解海星),所以可以矩陣快速冪。


講完了,還有點細節,-1表示不連通而不能用0表示,因爲有的聯通但快樂值爲0。還有GetFail的寫法什麼的。還有中途糾結了幾千萬遍根是11號店還是00號點,最後選擇了像11號店屈服。


我不會告訴你我的海口複習法有多好用。
海口複習法

代碼

#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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章