题目传送门

题目大意

给出一个残缺的字符串,每个位置都 \(\in[0,9]\)。有 \(m\) 中贡献,即 \(s,k\),表示该字符串中没出现一次 \(s\),贡献便乘上 \(k\)。最后对贡献求 \(c\) 次根,其中 \(c\) 是总出现次数。求贡献的最大值。

字符串长度以及贡献字符串长度之和 \(\le 1500\)

思路

首先你需要想到我们可以全部取 \(\ln\),然后每次贡献就是 \(+k\),求根就是 \(/c\),于是问题就是最大化:

\[\frac{\sum k}{c}
\]

然后你对这个二分,判断条件就是:

\[\sum_{k-mid}>0
\]

于是我们可以在 AC 自动机上进行dp,即设 \(f_{i,j}\) 表示到第 \(i\) 个字符串对应自动机上状态j时的最大贡献,转移显然。

于是,我们就可以在 \(nl\log w\) 的时间复杂度内解决这个问题。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define MAXN 2005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} int n,m;
int g[MAXN][MAXN][2];
double dp[MAXN][MAXN];
char s1[MAXN],s2[MAXN],ans[MAXN]; struct Auto{
double val[MAXN];
int cnt,ch[MAXN][10],fail[MAXN],sum[MAXN];
void Insert (char *s,double v){
int now = 0;
for (Int i = 1;s[i];++ i){
if (!ch[now][s[i] - '0']) ch[now][s[i] - '0'] = ++ cnt;
now = ch[now][s[i] - '0'];
}
sum[now] ++,val[now] += v;
}
void Build (){
queue <int> q;
while (!q.empty()) q.pop ();
for (Int i = 0;i < 10;++ i) if (ch[0][i]) q.push (ch[0][i]);
while (!q.empty()){
int u = q.front();q.pop ();
sum[u] += sum[fail[u]],val[u] += val[fail[u]];
for (Int i = 0;i < 10;++ i){
if (ch[u][i]) fail[ch[u][i]] = ch[fail[u]][i],q.push (ch[u][i]);
else ch[u][i] = ch[fail[u]][i];
}
}
}
double Work (double v){
for (Int i = 0;i <= cnt;++ i) val[i] -= sum[i] * v;
for (Int i = 0;i <= n;++ i) for (Int j = 0;j <= cnt;++ j) dp[i][j] = -1e6;
dp[0][0] = 0;
for (Int i = 0;i < n;++ i)
for (Int j = 0;j <= cnt;++ j)
if (dp[i][j] > -1e5)
for (Int k = 0;k < 10;++ k)
if (s1[i] == '.' || s1[i] == k + '0'){
int t = ch[j][k];
if (dp[i + 1][t] < dp[i][j] + val[t]){
dp[i + 1][t] = dp[i][j] + val[t];
g[i + 1][t][0] = k,g[i + 1][t][1] = j;
}
}
for (Int i = 0;i <= cnt;++ i) val[i] += sum[i] * v;
int pos = 0;for (Int i = 0;i <= cnt;++ i) if (dp[n][i] > dp[n][pos]) pos = i;
for (Int i = n,now = pos;i;-- i) ans[i] = g[i][now][0] + '0',now = g[i][now][1];
return dp[n][pos];
}
}T; signed main(){
read (n,m);
scanf ("%s",s1);
for (Int i = 1,v;i <= m;++ i){
scanf ("%s",s2 + 1),read (v);
T.Insert (s2,log (v));
}
T.Build();
double l = 0,r = 1e9;
while (r - l > 1e-3){
double mid = (l + r) / 2;
if (T.Work(mid) > 0) l = mid;
else r = mid;
}
T.Work(l),printf ("%s",ans + 1);
return 0;
}

题解 [BJOI2019]奥术神杖的更多相关文章

  1. [BJOI2019]奥术神杖(分数规划,动态规划,AC自动机)

    [BJOI2019]奥术神杖(分数规划,动态规划,AC自动机) 题面 洛谷 题解 首先乘法取\(log\)变加法,开\(c\)次根变成除\(c\). 于是问题等价于最大化\(\displaystyle ...

  2. luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP)

    luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP) Luogu 题解时间 难点在于式子转化,设有c个满足的子串,即求最大的 $ ans = \sqrt[c]{\prod_{ ...

  3. [BJOI2019]奥术神杖——AC自动机+DP+分数规划+二分答案

    题目链接: [BJOI2019]奥术神杖 答案是$ans=\sqrt[c]{\prod_{i=1}^{c}v_{i}}=(\prod_{i=1}^{c}v_{i})^{\frac{1}{c}}$. 这 ...

  4. 【题解】Luogu P5319 [BJOI2019]奥术神杖

    原题传送门 题目让我们最大化\(val=\sqrt[k]{\prod_{i=1}^k w_i}\),其中\(k\)是咒语的个数,\(w_i\)是第\(i\)个咒语的神力 看着根号和累乘不爽,我们两边同 ...

  5. [BJOI2019]奥术神杖

    https://www.luogu.org/problemnew/show/P5319 题解 首先观察我们要求的答案的形式: \[ \biggl(\prod V_i \biggr)^x\ \ \ x= ...

  6. [BJOI2019]奥术神杖(分数规划+AC自动机+DP)

    题解:很显然可以对权值取对数,然后把几何平均值转为算术平均值,然后很显然是分数规划.先对每个模式串建立AC自动机,每个节点w[i],sz[i]分别表示以其为前缀的字符串,然后再二分最优解k,然后w[i ...

  7. luogu P5319 [BJOI2019]奥术神杖

    传送门 要求的东西带个根号,这玩意叫几何平均数,说到平均数,我们就能想到算术平均数(就是一般意义下的平均数),而这个东西是一堆数之积开根号,所以如果每个数取对数,那么乘法会变成加法,开根号变成除法,所 ...

  8. #loj3089 [BJOI2019]奥术神杖

    卡精度好题 最关键的一步是几何平均数的\(ln\)等于所有数字取\(ln\)后的算术平均值 那么现在就变成了一个很裸的01分数规划问题,一个通用的思路就是二分答案 现在来考虑二分答案的底层怎么写 把所 ...

  9. [BJOI2019]奥术神杖(AC自动机,DP,分数规划)

    题目大意: 给出一个长度 $n$ 的字符串 $T$,只由数字和点组成.你可以把每个点替换成一个任意的数字.再给出 $m$ 个数字串 $S_i$,第 $i$ 个权值为 $t_i$. 对于一个替换方案,这 ...

随机推荐

  1. webpack编译后的代码如何在浏览器执行

    浏览器是无法直接使用模块之间的commonjs或es6,webpack在打包时做了什么处理,才能让浏览器能够执行呢,往下看吧. 使用commonjs语法 先看下写的代码, app.js minus.j ...

  2. java和js中for循环的区别

    java中for循环,先执行语句后循环 for (int i=1;i<10;i++){ for(int b=1;b<=i;b++){ System.out.print(b+"*& ...

  3. 高性能利器:CDN我建议你好好学一下!

    硬核干货分享,欢迎关注[Java补习课]成长的路上,我们一起前行 ! <高可用系列文章> 已收录在专栏,欢迎关注! CDN 概述 CDN 全称 Content Delivery Netwo ...

  4. 第17章-x86-64寄存器

    不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Architecture),也可以称为指令集(instruction set).Intel将x86系列CP ...

  5. Redis中关于key的操作指令

    1.keys: 例如: 2.exists 3.move 将指定的数据移动到指定的库 4.expire 5.tt1 6.type 7.rename 8.del

  6. redis 主从复制详解

    引言 我们之前操作 Redis 都是单机版,但是实际应用中没人使用单机版,都是搭建集群的方式.这篇文章要介绍的主从复制,是指将一台 Redis 服务器的数据,复制到其他 Redis 服务器,我们将前者 ...

  7. RIAD配置

    一.RIAD 磁盘阵列介绍 二.阵列卡介绍 三.案例举例   一.RAID磁盘阵列介绍 是Redundant Array of Independent Disks的缩写,中文简称为独立冗余磁盘阵列 把 ...

  8. mysql中通过sql语句查询指定数据表的字段信息

      mysql数据库在安装完成时,自动创建了information_schema.mysql.test这三个数据库.其中,information_schema记录了创建的所有数据库的相关信息,因此可以 ...

  9. freeswitch刷新网关方法

    1.freeswitch xml配置文件新增网关后,使其生效,可以重启freeswitch或者使用命令方式 fs_cli -H 127.0.0.1 -P 8021 -p hmzj -x sofia p ...

  10. Vue3.x全家桶+vite+TS-构建Vue3基本框架

    目录 一.搭建基础项目 1.vite创建项目 3.运行项目 2.环境变量设置介绍 vite配置多环境打包 二.配置Router 1.安装路由 2.配置路由 3.引入 三.配置Vuex 1.安装vuex ...