题意
题解
求 ∏ V i c \sqrt[c]{\prod V_i} c∏Vi 的最大值。若直接计算,需要上高精度;暴力枚举更换的宝石,总时间复杂度 O ( 1 0 N ) O(10^N) O(10N)。将答案取对数进行考虑 ∑ ln V i c \frac{\sum \ln V_i}{c} c∑lnVi 则转化为 0 / 1 0/1 0/1 分数规划问题。二分答案的对数值,判定是否存在满足下式的方案 ∑ ( ln V i − x ) ≥ 0 \sum (\ln V_i-x)\geq 0 ∑(lnVi−x)≥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 ∑(lnVi−x)>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;
}