题解 [BJOI2019]奥术神杖
题目大意
给出一个残缺的字符串,每个位置都 \(\in[0,9]\)。有 \(m\) 中贡献,即 \(s,k\),表示该字符串中没出现一次 \(s\),贡献便乘上 \(k\)。最后对贡献求 \(c\) 次根,其中 \(c\) 是总出现次数。求贡献的最大值。
字符串长度以及贡献字符串长度之和 \(\le 1500\)
思路
首先你需要想到我们可以全部取 \(\ln\),然后每次贡献就是 \(+k\),求根就是 \(/c\),于是问题就是最大化:
\]
然后你对这个二分,判断条件就是:
\]
于是我们可以在 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]奥术神杖的更多相关文章
- [BJOI2019]奥术神杖(分数规划,动态规划,AC自动机)
[BJOI2019]奥术神杖(分数规划,动态规划,AC自动机) 题面 洛谷 题解 首先乘法取\(log\)变加法,开\(c\)次根变成除\(c\). 于是问题等价于最大化\(\displaystyle ...
- luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP)
luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP) Luogu 题解时间 难点在于式子转化,设有c个满足的子串,即求最大的 $ ans = \sqrt[c]{\prod_{ ...
- [BJOI2019]奥术神杖——AC自动机+DP+分数规划+二分答案
题目链接: [BJOI2019]奥术神杖 答案是$ans=\sqrt[c]{\prod_{i=1}^{c}v_{i}}=(\prod_{i=1}^{c}v_{i})^{\frac{1}{c}}$. 这 ...
- 【题解】Luogu P5319 [BJOI2019]奥术神杖
原题传送门 题目让我们最大化\(val=\sqrt[k]{\prod_{i=1}^k w_i}\),其中\(k\)是咒语的个数,\(w_i\)是第\(i\)个咒语的神力 看着根号和累乘不爽,我们两边同 ...
- [BJOI2019]奥术神杖
https://www.luogu.org/problemnew/show/P5319 题解 首先观察我们要求的答案的形式: \[ \biggl(\prod V_i \biggr)^x\ \ \ x= ...
- [BJOI2019]奥术神杖(分数规划+AC自动机+DP)
题解:很显然可以对权值取对数,然后把几何平均值转为算术平均值,然后很显然是分数规划.先对每个模式串建立AC自动机,每个节点w[i],sz[i]分别表示以其为前缀的字符串,然后再二分最优解k,然后w[i ...
- luogu P5319 [BJOI2019]奥术神杖
传送门 要求的东西带个根号,这玩意叫几何平均数,说到平均数,我们就能想到算术平均数(就是一般意义下的平均数),而这个东西是一堆数之积开根号,所以如果每个数取对数,那么乘法会变成加法,开根号变成除法,所 ...
- #loj3089 [BJOI2019]奥术神杖
卡精度好题 最关键的一步是几何平均数的\(ln\)等于所有数字取\(ln\)后的算术平均值 那么现在就变成了一个很裸的01分数规划问题,一个通用的思路就是二分答案 现在来考虑二分答案的底层怎么写 把所 ...
- [BJOI2019]奥术神杖(AC自动机,DP,分数规划)
题目大意: 给出一个长度 $n$ 的字符串 $T$,只由数字和点组成.你可以把每个点替换成一个任意的数字.再给出 $m$ 个数字串 $S_i$,第 $i$ 个权值为 $t_i$. 对于一个替换方案,这 ...
随机推荐
- webpack编译后的代码如何在浏览器执行
浏览器是无法直接使用模块之间的commonjs或es6,webpack在打包时做了什么处理,才能让浏览器能够执行呢,往下看吧. 使用commonjs语法 先看下写的代码, app.js minus.j ...
- java和js中for循环的区别
java中for循环,先执行语句后循环 for (int i=1;i<10;i++){ for(int b=1;b<=i;b++){ System.out.print(b+"*& ...
- 高性能利器:CDN我建议你好好学一下!
硬核干货分享,欢迎关注[Java补习课]成长的路上,我们一起前行 ! <高可用系列文章> 已收录在专栏,欢迎关注! CDN 概述 CDN 全称 Content Delivery Netwo ...
- 第17章-x86-64寄存器
不同的CPU都能够解释的机器语言的体系称为指令集架构(ISA,Instruction Set Architecture),也可以称为指令集(instruction set).Intel将x86系列CP ...
- Redis中关于key的操作指令
1.keys: 例如: 2.exists 3.move 将指定的数据移动到指定的库 4.expire 5.tt1 6.type 7.rename 8.del
- redis 主从复制详解
引言 我们之前操作 Redis 都是单机版,但是实际应用中没人使用单机版,都是搭建集群的方式.这篇文章要介绍的主从复制,是指将一台 Redis 服务器的数据,复制到其他 Redis 服务器,我们将前者 ...
- RIAD配置
一.RIAD 磁盘阵列介绍 二.阵列卡介绍 三.案例举例 一.RAID磁盘阵列介绍 是Redundant Array of Independent Disks的缩写,中文简称为独立冗余磁盘阵列 把 ...
- mysql中通过sql语句查询指定数据表的字段信息
mysql数据库在安装完成时,自动创建了information_schema.mysql.test这三个数据库.其中,information_schema记录了创建的所有数据库的相关信息,因此可以 ...
- freeswitch刷新网关方法
1.freeswitch xml配置文件新增网关后,使其生效,可以重启freeswitch或者使用命令方式 fs_cli -H 127.0.0.1 -P 8021 -p hmzj -x sofia p ...
- Vue3.x全家桶+vite+TS-构建Vue3基本框架
目录 一.搭建基础项目 1.vite创建项目 3.运行项目 2.环境变量设置介绍 vite配置多环境打包 二.配置Router 1.安装路由 2.配置路由 3.引入 三.配置Vuex 1.安装vuex ...