[BJOI2019]奥术神杖

/*
用 log去掉次方然后变成裸的01分数规划问题
具体来说是要给每个trans赋值, 然后跑取max转移吧
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<iostream>
#include<cmath>
#define ll long long
#define M 1520
#define mmp make_pair
using namespace std;
int read() {
    int nm = 0, f = 1;
    char c = getchar();
    for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
    return nm * f;
}
char s[M], tmp[M];
double f[M][M];
int n, m, pre[M][M], note[M], as[M][M];
struct AC {
#define ch son
    int fail[M], son[M][10], end[M], cnt, fa[M], biao[M];
    double sum[M], trans[M], v[M], cost[M];
    void insert(double x) {
        int now = 0, len = strlen(tmp + 1);
        for(int i = 1; i <= len; i++) {
            if(ch[now][tmp[i] - '0'] == 0) ch[now][tmp[i] - '0'] = ++cnt, fa[cnt] = now, biao[cnt] = tmp[i] - '0';
            now = ch[now][tmp[i] - '0'];
        }
        v[now] += x, end[now]++;
    }
    void getfail() {
        queue<int> q;
        for(int i = 0; i < 10; i++) if(son[0][i] != 0) fail[son[0][i]] = 0, q.push(son[0][i]), trans[son[0][i]] = v[son[0][i]], sum[son[0][i]] = end[son[0][i]];
        while(!q.empty()) {
            int now = q.front();
            q.pop();
            for(int i = 0; i < 10; i++) {
                if(son[now][i] != 0) {
                    fail[son[now][i]] = son[fail[now]][i];
                    trans[son[now][i]] += trans[son[fail[now]][i]];
                    sum[son[now][i]] += sum[son[fail[now]][i]];
                    if(end[son[now][i]]) trans[son[now][i]] += v[son[now][i]], sum[son[now][i]] += end[son[now][i]];
                    q.push(son[now][i]);
                } else son[now][i] = son[fail[now]][i];
            }
        }
        //  for(int i = 0; i < 10; i++) if(ch[0][i] == 0) biao[0] = i;
    }

} ac;
const double inf = pow(2, 70);

void cl() {
    for(int i = 0; i <= n; i++) for(int j = 0; j <= ac.cnt; j++) f[i][j] = -inf;//, pre[i][j] = 0, as[i][j] = 0;
}

void dp() {
    f[0][0] = 0;
    for(int i = 1; i <= n; i++) {
        if(s[i] == '.') {
            for(int j = 0; j <= ac.cnt; j++) {
                for(int k = 0; k < 10; k++) {
                    if(f[i][ac.son[j][k]] < f[i - 1][j] + ac.cost[ac.son[j][k]])
                        f[i][ac.son[j][k]] = f[i - 1][j] + ac.cost[ac.son[j][k]],
                                             pre[i][ac.son[j][k]] = j,
                                                     as[i][ac.son[j][k]] = k;
                }
            }
        } else {
            int k = s[i] - '0';
            for(int j = 0; j <= ac.cnt; j++) {
                if(f[i][ac.son[j][k]] < f[i - 1][j] + ac.cost[ac.son[j][k]])
                    f[i][ac.son[j][k]] = f[i - 1][j] + ac.cost[ac.son[j][k]],
                                         pre[i][ac.son[j][k]] = j,
                                                 as[i][ac.son[j][k]] = k;
            }
        }
    }
}

bool check(double x) {
    for(int i = 1; i <= ac.cnt; i++) ac.cost[i] = ac.trans[i] - x * ac.sum[i];
    cl();
    dp();
    for(int i = 0; i <= ac.cnt; i++) {
        if(f[n][i] > 0) {
            int now = i;
            for(int j = n; j >= 1; j--) {
                note[j] = as[j][now];
                now = pre[j][now];
            }
            return true;
        }
    }
    return false;
}


void getans() {
    double l = 0, r = 1.0 * 1e8;
    int up;
    if(n <= 501) up = 120;
    else up = 42;
    for(int j = 1; j <= up; j++) {
        double mid = (l + r) / 2;
        if(check(mid)) l = mid;
        else r = mid;
    }
//  printf("%.10lf\n", exp(l));
}

int main() {
    n = read(), m = read();
    scanf("%s", s + 1);
    for(int i = 1; i <= m; i++) {
        scanf("%s", tmp + 1);
        double v = read();
        v = log(v);
        ac.insert(v);
    }
    for(int i = 1; i <= n; i++) note[i] = s[i] - '0';
    ac.getfail();
    getans();
    for(int i = 1; i <= n; i++) cout << note[i];
    cout << "\n";
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/luoyibujue/p/10746965.html