# LOJ 3089: 洛谷 P5319: 「BJOI2019」奥术神杖

### 题意简述：

$1\le n,s\le 1501$$1\le V_i\le \max V=10^9$，其中 $\displaystyle s=\sum_{i=1}^{m}|S_i|$

### 题解：

\begin{aligned}\frac{1}{c}\sum_{i=1}^{c}\ln w_i&>\mathrm{mid}\\\sum_{i=1}^{c}\ln w_i&>c\cdot\mathrm{mid}\\\sum_{i=1}^{c}(\ln w_i-\mathrm{mid})&>0\end{aligned}

DP 时每个小串的权值设为 $\ln V_i-\mathrm{mid}$，注意要记录最佳转移点，以输出方案。

#include <cstdio>
#include <cmath>

typedef double f64;
const int MN = 1505, Sig = 10;
const f64 eps = 1e-6, inf = 1e99;

int N, M;
char T[MN];

char str[MN];
int ch[MN][Sig], fail[MN], sum[MN], cnt;
f64 val[MN];

inline void Insert(char *s, f64 v) {
int now = 0;
for (; *s; ++s) {
if (!ch[now][*s & 15]) ch[now][*s & 15] = ++cnt;
now = ch[now][*s & 15];
} ++sum[now], val[now] += v;
}

int que[MN], l, r;
void BuildAC() {
fail[0] = -1;
que[l = r = 1] = 0;
while (l <= r) {
int u = que[l++];
for (int i = 0; i < Sig; ++i) {
if (ch[u][i]) {
int x = fail[u];
while (~x && !ch[x][i]) x = fail[x];
if (~x) fail[ch[u][i]] = ch[x][i];
que[++r] = ch[u][i];
}
else if (~fail[u]) ch[u][i] = ch[fail[u]][i];
}
}
for (int i = 2; i <= r; ++i)
sum[que[i]] += sum[fail[que[i]]],
val[que[i]] += val[fail[que[i]]];
}

f64 f[MN][MN];
int g[MN][MN][2];
char AT[MN];
inline f64 DP(f64 V) {
for (int j = 0; j <= cnt; ++j) val[j] -= sum[j] * V;
for (int i = 0; i <= N; ++i)
for (int j = 0; j <= cnt; ++j)
f[i][j] = -inf;
f[0][0] = 0;
for (int i = 0; i < N; ++i) {
for (int j = 0; j <= cnt; ++j) {
if (f[i][j] == -inf) continue;
if (T[i] == '.') {
for (int k = 0; k < Sig; ++k) {
int _j = ch[j][k];
if (f[i + 1][_j] < f[i][j] + val[_j])
f[i + 1][_j] = f[i][j] + val[_j],
g[i + 1][_j][0] = j,
g[i + 1][_j][1] = k;
}
}
else {
int _j = ch[j][T[i] & 15];
if (f[i + 1][_j] < f[i][j] + val[_j])
f[i + 1][_j] = f[i][j] + val[_j],
g[i + 1][_j][0] = j,
g[i + 1][_j][1] = T[i] & 15;
}
}
}
for (int j = 0; j <= cnt; ++j) val[j] += sum[j] * V;
int ans = 0;
for (int j = 1; j <= cnt; ++j)
if (f[N][j] > f[N][ans]) ans = j;
for (int i = N, j = ans; i >= 1; --i)
AT[i - 1] = g[i][j][1] | 48,
j = g[i][j][0];
return f[N][ans];
}

int main() {
scanf("%d%d", &N, &M);
scanf("%s", T);
for (int i = 1; i <= M; ++i) {
f64 v;
scanf("%s%lf", str, &v);
Insert(str, log(v));
}
BuildAC();
f64 l = 0, r = log(1e9 + 5), mid, ans = 0;
while (r - l > eps) {
mid = (l + r) / 2;
if (DP(mid) > 0) ans = mid, l = mid;
else r = mid;
}
DP(ans);
printf("%s\n", AT);
return 0;
}

posted @ 2019-04-27 21:13  粉兔  阅读(...)  评论(...编辑  收藏