题目传送门

题目大意

给出一个残缺的字符串,每个位置都 \(\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. redis rpoplpush列表转移元素

    文档出处:redisdoc.com/list/rpoplpush.html模式: 安全的队列 Redis的列表经常被用作队列(queue),用于在不同程序之间有序地交换消息(message).一个客户 ...

  2. 恶意软件开发——shellcode执行的几种常见方式

    一.什么是shellcode? shellcode是一小段代码,用于利用软件漏洞作为有效载荷.它之所以被称为"shellcode",是因为它通常启动一个命令shell,攻击者可以从 ...

  3. Python之sqlite3模块

    python自带有sqlite3模块,该模块可以方便我们操作sqlite数据库,下面一起跟随示例了解sqlite3模块的具体用法. import sqlite3 # 连接数据库 connection ...

  4. Python之uiautomation模块-获取CMD窗口中所打印的文字信息

    当我们想以自动化的方式操作软件,以提高办公或测试效率时,有许多成熟的工具,比如针对Web端应用的Selenium.针对移动端应用的Appium.那么,PC端(Windows)桌面应用,又改如何处理呢? ...

  5. Charles-抓取https请求

    在未经设置之前,Charles是无法抓取https请求的,会出现unknown的标识.我们可以通过以下两步设置,解决该问题. 第一步:安装证书 https是在http的基础上加入ssl层,通过ssl来 ...

  6. tar 命令简介

    tar命令可以为linux的文件和目录创建档案.利用tar,可以为某一特定文件创建档案(备份文件),也可以在档案中改变文件,或者向档案中加入新的文件.tar最初被用来在磁带上创建档案,现在,用户可以在 ...

  7. Spring Cloud Gateway 学习+实践

    官网上给出的Spring Cloud Gateway特性如下图所示: 翻译过来就是: 基于 Spring Framework 5 ,Project Reactor 以及 Spring Boot 2.0 ...

  8. weblogic之XXE利用与分析

    weblogic之XXE利用与分析 本篇文章漏洞环境使用p神的CVE-2018-2628 本机IP:192.168.202.1 被攻击主机IP:192.168.202.129 一. xxer工具 1. ...

  9. private关键字理解

    private 意思: 私有的 私人的 不公开的 private 是一个修饰符可以用来修饰成员变量和方法 被private修饰的成员变量或成员方法,只能在本类中访问,针对private修饰的成员变量, ...

  10. leetcode8 字符串转换为整数

    最笨的办法实现 一步步判断 /** * @param {string} s * @return {number} */ var myAtoi = function(s) { s = s.trim() ...