题解 [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$. 对于一个替换方案,这 ...
随机推荐
- 查询ES6兼容的网站
http://kangax.github.io/compat-table/es6/ 查询es6兼容的网站
- Linux基础——安装以及常用命令
Linux基础--常用命令 1.安装Vmware 进入VMware官网: https://www.vmware.com/cn.html下载安装 镜像推荐网址下载:https://www.linux. ...
- ES6扩展运算符(三点运算符)...的用法
1. 第一个叫做 展开运算符(spread operator),作用是和字面意思一样,就是把东西展开.可以用在array和object上都行. let a = [1,2,3]; let b = [0, ...
- FPGA nios通过驱动LCD12864实现菜单界面和uart串口通信
因为csdn无法插入视频,无法展示我这个实现的效果,这里我截了一些图,应该基本上也能明白了: 基本功能就是如图片所示,里面采用了菜单结构(这里编程需要一定得c语言编程技巧与数据结构知识),gpa是什么 ...
- maven下载出错
求解
- blender skin modifier 太好玩了
https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/skin.html 只需要像画火柴人一样把点连起来,skin ...
- Appium问题解决方案(10)- Original error: Swipe did not complete successfully
背景 从搜索页面返回首页之后,执行 swipe 滑动操作,但是报错了,如上图 解决方法 只需要在第一次 swipe 之前加个 sleep,强制等待即可 备注 这种解决方案其实不好,强制等待能少用就少 ...
- C# AutoMaper简单使用
AutoMapper它是一种对象与对象之间的映射器,让AutoMapper有意思的就是在于它提供了一些将类型A映射到类型B这种无聊的实例,只要B遵循AutoMapper已经建立的惯例,那么大多数情况下 ...
- FileWriter文件文件字符输出流写入存储数据
1.FileWriter文件字符输出流-写入-存储数据 其中,流关闭之后再调用会报IOException; 其中,与文件字符输入流-写出-读取数据 和 字节输出流-写入-存储数据 不同的是,要先flu ...
- Java XXE漏洞典型场景分析
本文首发于oppo安全应急响应中心: https://mp.weixin.qq.com/s?__biz=MzUyNzc4Mzk3MQ==&mid=2247485488&idx=1&am ...