其他分享
首页 > 其他分享> > 2020 ACM-ICPC澳门区域赛 B题 Boring Problem 题解

2020 ACM-ICPC澳门区域赛 B题 Boring Problem 题解

作者:互联网

B Boring Problem


题意:
给出 n ≤ 100 n \le 100 n≤100个长度为 m ≤ 100 m \le 100 m≤100的串 T i T_i Ti​和一个串 R R R,对每个 R R R的前缀,每次在其末尾以 P i P_i Pi​的概率添加字符 i ≤ k ≤ 26 i \le k \le 26 i≤k≤26,当生成的字符串 S S S中存在一个 T i T_i Ti​为 S S S的子串时停止,求生成的字符串的期望长度。

题解:

对 T i T_i Ti​建立AC自动机,设 t r i , j tr_{i,j} tri,j​表示从节点 i i i添加字符 j j j到达的节点, E i E_i Ei​表示从节点 i i i开始,停止时末尾添加字符个数的期望。
最后用字符串 R R R跑一边AC自动机计算答案。

在AC自动机上,对于所有叶子节点, E l e a f = 0 E_{leaf} = 0 Eleaf​=0;对于所有非叶子节点 u u u,转移为 E u = 1 + ∑ i = 1 k E t r u , i P i E_u=1 +\sum_{i=1}^k\frac{E_{tr_{u,i}}}{P_i} Eu​=1+∑i=1k​Pi​Etru,i​​​;注意到转移有环,需要高斯消元, O ( n m ) O(nm) O(nm)个未知数和等式,复杂度 O ( n 3 m 3 ) O(n^3m^3) O(n3m3)。

将未知数个数降为 O ( n ) O(n) O(n):
设 E q u a t i o n i Equation_i Equationi​表示 E i = E q u a t i o n i E_i=Equation_i Ei​=Equationi​,其中 E q u a t i o n i Equation_i Equationi​由常数和 O ( n ) O(n) O(n)个未知数组成。
1.初始化根节点 E r o o t E_{root} Eroot​为一个未知数,当前未知数总数为1。
2.按深度递增遍历AC自动机。
3.设当前深度为 d e p dep dep,深度 ≤ d e p \le dep ≤dep的节点的 E q u a t i o n i Equation_i Equationi​都已知;
对于节点 u u u,若是叶子结点,则将 E u = 0 = E q u a t i o n u E_u=0=Equation_u Eu​=0=Equationu​扔进方程组;
否则,设 s o n son son为节点u的所有原字典树的儿子,设 s o n son son的大小为 s z sz sz,将其中 s z − 1 sz-1 sz−1个 E v E_v Ev​设为新的未知数,此时 E q u a t i o n v = E v Equation_v=E_v Equationv​=Ev​;并根据上述转移移项可得剩下一个儿子的 E q u a t i o n v Equation_v Equationv​,即 E v = E u − 1 − ∑ i = 1 k E t r u , i P i [ t r u , i ≠ v ] P v E_v=\frac{E_u-1-\sum_{i=1}^k\frac{E_{tr_{u,i}}}{P_i}[tr_{u,i≠v}]}{P_v} Ev​=Pv​Eu​−1−∑i=1k​Pi​Etru,i​​​[tru,i​=v​]​。
转移复杂度 O ( n 2 m k ) O(n^2mk) O(n2mk)。

设叶子的个数为 Z Z Z,等式的个数为叶子的个数 Z Z Z,未知数的个数为 1 1 1(根)+ Z − 1 Z-1 Z−1(分叉的个数) = Z =Z =Z。
高斯消元复杂度 O ( Z 3 ) O(Z^3) O(Z3)。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 1e4 + 10;
constexpr int mod = 1e9 + 7;

ll power(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1)res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

void add(int &a, int b) { a = (a + b) % mod; }

pair<bool, vector<int>> Gauss(vector<vector<int>> a) {
    int n = a.size() - 1;
    for (int y = 1; y <= n; y++) {
        int mx = y;
        for (int x = y; x <= n; x++)if (a[x][y] > a[mx][y])mx = x;
        if (!a[mx][y])return {0, {0}};
        int ik = power(a[mx][y], mod - 2);
        for (int i = y; i <= n + 1; i++)a[mx][i] = (ll) a[mx][i] * ik % mod;
        for (int x = 1; x <= n; x++)
            if (x != mx && a[x][y]) {
                int k = a[x][y];
                for (int i = y; i <= n + 1; i++) {
                    add(a[x][i], mod - (ll) a[mx][i] * k % mod);
                }
            }
        swap(a[y], a[mx]);
    }
    vector<int> res(n + 1);
    for (int i = 1; i <= n; i++)res[i] = a[i][n + 1];
    return {1, res};
}

vector<int> operator+(vector<int> a, vector<int> b) {
    int n = max(a.size(), b.size());
    a.resize(n), b.resize(n);
    for (int i = 0; i < n; i++)add(a[i], b[i]);
    return a;
}

vector<int> operator-(vector<int> a, vector<int> b) {
    int n = max(a.size(), b.size());
    a.resize(n), b.resize(n);
    for (int i = 0; i < n; i++)add(a[i], mod - b[i]);
    return a;
}

vector<int> operator*(vector<int> a, ll b) {
    b = (b % mod + mod) % mod;
    for (auto &I:a)I = I * b % mod;
    return a;
}

int n, m, k;
vector<int> Equation[N];
int E[N];
int p[26];

struct AC {
    int tr[N][26], tot, dep[N];
    int fail[N];
    bool key[N];

    int insert(char s[]) {
        int u = 0;
        for (int i = 1; s[i]; i++) {
            int c = s[i] - 'a';
            if (!tr[u][c])tr[u][c] = ++tot;
            u = tr[u][c];
            if (key[u])break;
            dep[u] = i;
        }
        key[u] = 1;
        return u;
    }

    void build() {
        queue<int> q;
        for (int i = 0; i < k; i++)if (tr[0][i])q.push(tr[0][i]);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (int i = 0; i < k; i++) {
                if (tr[u][i]) {
                    fail[tr[u][i]] = tr[fail[u]][i], q.push(tr[u][i]);
                } else
                    tr[u][i] = tr[fail[u]][i];
            }
        }
    }

    void calc_e() {
        queue<int> q;
        int tot_unknown = 0;//当前未知数个数
        //Equation[u][0] 表示常数 Equation[u][1,tot_unknown]表示未知数的系数
        // 初始化根节点E_root 为未知数
        q.push(0);
        Equation[0].assign({0, 1});
        tot_unknown++;
        vector<vector<int>> a(1);//等式组
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            if (key[u]) {
                a.push_back(Equation[u]);
                continue;//one equation
            }
            vector<int> son;//字典树上的儿子
            int p_son = -1;//第0个儿子的字符期望
            for (int i = 0; i < k; i++) {
                if (dep[tr[u][i]] > dep[u]) {
                    son.push_back(tr[u][i]);
                    q.push(tr[u][i]);
                    if (p_son == -1)p_son = p[i];
                }
            }

            for (int i = 1; i < son.size(); i++) {//新增sz-1 个未知数
                Equation[son[i]].resize(tot_unknown + 1);
                Equation[son[i]].push_back(1);
                tot_unknown++;
            }

            //求son[0]的Equation
            int v = son[0];
            Equation[v] = Equation[u];
            add(Equation[v][0], mod - 1);
            for (int i = 0; i < k; i++)
                if (tr[u][i] != v) {
                    Equation[v] = Equation[v] - Equation[tr[u][i]] * p[i];
                }
            Equation[v] = Equation[v] * power(p_son, mod - 2);
        }
        for (auto &I:a) {
            I.resize(tot_unknown + 2);
            I.back() = (mod - I.front()) % mod;
        }

        vector<int> res = Gauss(a).second;
        for (int i = 1; i <= tot; i++) {
            for (int j = 1; j < Equation[i].size(); j++)
                add(E[i], (ll) res[j] * Equation[i][j] % mod);
            add(E[i], Equation[i][0]);//加上常数
        }
    }

    void print_ans(char *s) {
        int u = 0;
        for (int i = 1; s[i]; i++) {
            u = tr[u][s[i] - 'a'];
            cout << (E[u] + i) % mod << "\n";
        }
    }
} ac;
char s[N];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> k;
    for (int i = 0; i < k; i++)cin >> p[i], p[i] = p[i] * power(100, mod - 2) % mod;
    for (int i = 0; i < n; i++) {
        cin >> s + 1;
        ac.insert(s);
    }
    ac.build();
    ac.calc_e();
    cin >> s + 1;
    ac.print_ans(s);
}

标签:int,题解,Equation,tr,ACM,son,++,Boring,mod
来源: https://blog.csdn.net/dadaaklsjdlka/article/details/117434556