P5319 [BJOI2019] 二分 + AC 自动机 + DP

题意

传送门 P5319 [BJOI2019]奥术神杖

题解

∏ V i c \sqrt[c]{\prod V_i} cVi 的最大值。若直接计算,需要上高精度;暴力枚举更换的宝石,总时间复杂度 O ( 1 0 N ) O(10^N) O(10N)。将答案取对数进行考虑 ∑ ln ⁡ V i c \frac{\sum \ln V_i}{c} clnVi 则转化为 0 / 1 0/1 0/1 分数规划问题。二分答案的对数值,判定是否存在满足下式的方案 ∑ ( ln ⁡ V i − x ) ≥ 0 \sum (\ln V_i-x)\geq 0 (lnVix)0 建立 A C AC AC 自动机, D P DP DP 进行判定。具体而言, d p [ i ] [ j ] dp[i][j] dp[i][j] 代表字符串 [ 1 , i ] [1,i] [1,i] 后缀状态为 j j j 时可以取到的最大值 ,在 A C AC AC 自动机上进行状态转移。因为后缀中存在前缀不包含任一咒语的状态,这类状态无论 x x x 取何值,都赋值为 0 0 0,那么二分的判断条件需要改变为 ∑ ( ln ⁡ V i − x ) > 0 \sum (\ln V_i-x)> 0 (lnVix)>0

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1505, bs = 10;
const double eps = 1e-3, inf = 2e15;
int N, M, Q[maxn];
int tot, trie[maxn][bs], fail[maxn], cnt[maxn];
int chs[maxn][maxn], pre[maxn][maxn];
char T[maxn], S[maxn];
double sum[maxn], val[maxn], dp[maxn][maxn];

inline void insert(char *s, double v)
{
    
    
    int len = strlen(s + 1), p = 1;
    for (int i = 1; i <= len; ++i)
    {
    
    
        int c = s[i] - '0';
        if (!trie[p][c])
            trie[p][c] = ++tot;
        p = trie[p][c];
    }
    sum[p] += v, ++cnt[p];
}

void getFail()
{
    
    
    int l, r;
    fail[1] = 0, Q[l = r = 1] = 1;
    while (l <= r)
    {
    
    
        int p = Q[l++];
        sum[p] += sum[fail[p]], cnt[p] += cnt[fail[p]];
        for (int i = 0; i < bs; ++i)
        {
    
    
            int &x = trie[p][i], f = fail[p];
            if (x)
                fail[x] = f ? trie[f][i] : 1, Q[++r] = x;
            else
                x = f ? trie[f][i] : 1;
        }
    }
}

double judge(double x)
{
    
    
    for (int i = 1; i <= tot; ++i)
        val[i] = sum[i] - cnt[i] * x;
    for (int i = 0; i <= N; ++i)
        for (int j = 1; j <= tot; ++j)
            dp[i][j] = -inf;
    dp[0][1] = 0;
    for (int i = 1; i <= N; ++i)
        for (int j = 1, k; j <= tot; ++j)
            if (dp[i - 1][j] != -inf)
            {
    
    
                double t;
                if (T[i] != '.')
                {
    
    
                    k = trie[j][T[i] - '0'];
                    if ((t = dp[i - 1][j] + val[k]) > dp[i][k])
                        dp[i][k] = t, pre[i][k] = j;
                }
                else
                    for (int c = 0; c < bs; ++c)
                    {
    
    
                        k = trie[j][c];
                        if ((t = dp[i - 1][j] + val[k]) > dp[i][k])
                            dp[i][k] = t, chs[i][k] = c, pre[i][k] = j;
                    }
            }
    return *max_element(dp[N] + 1, dp[N] + tot + 1);
}

int main()
{
    
    
    scanf("%d%d", &N, &M);
    scanf("%s", T + 1);
    ++tot;
    for (int i = 1, v; i <= M; ++i)
        scanf("%s%d", S + 1, &v), insert(S, log(v));
    getFail();
    double lb = 0, ub = log(1e9) + 1;
    while (ub - lb > eps)
    {
    
    
        double mid = (lb + ub) / 2;
        if (judge(mid) > 0)
            lb = mid;
        else
            ub = mid;
    }
    judge(lb);
    int p = 0;
    for (int i = 1; i <= tot; ++i)
        if (!p || dp[N][i] > dp[N][p])
            p = i;
    for (int i = N; i; --i)
    {
    
    
        if (T[i] == '.')
            T[i] = chs[i][p] + '0';
        p = pre[i][p];
    }
    puts(T + 1);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/115236459