【BJOI 2019】奥术神杖

题意

你有一个长度为 $n$ 的模板串(由 $0-9$ 这 $10$ 个数字和通配符 $.$ 组成),还有 $m$ 个匹配串(只由 $0-9$ 这 $10$ 个数字组成),每个匹配串有一个魔力值 $v_i$。你要把模板串的每个 $.$ 都换成一个数字,使得模板串的魔力值最大。模板串的魔力值定义为:模板串中每出现一次任意一个匹配串 $s_i$,字符串的魔力就 $\times v_i$。最终魔力值开 $c$ 次方根,$c$ 为模板串中出现的匹配串的总数。

$1\le n,m,s\le 1501,\space 1\le v_i\le 10^9$

题解

王·能过就行·子健

显然只要三个 $10^9$ 大小的数乘起来就爆 $long\space long$ 了(即 $\prod v_i$ 会很大),而高精度开根既难写又爆复杂度(光乘法就爆时间了),所以不能直接按题目的公式求。

如果你没学过数学(比如我),可以把所有 $v_i$ 各自开 $c$ 次方根再相乘,但即使开 $long\space double$ 也会爆精度,不过可以拿 $80$ 分。

如果你学过数学,应该记得高一数学必修 $1$ 中有一章讲了关于 $log$ 的各种性质,其中有两条是

$$\log_a{MN} = \log_a{M}+\log_a{N}$$

$$\log_a{N}^k = k\times \log_a{N}$$

其中 $a$ 可以是任意实底数。

第一条式子中的 $MN$ 可以拓展成任意多个乘数,等号右边就会得到一堆 $log$ 值相加。简单地说就是因为幂值相乘等于指数相加(比如 $2^4$ 变成 $2^5$ 次方,值乘了 $2$,但指数只加了 $1$)。

具体证明可以去翻书。

 

把两个公式组合一下,就可以推这题的公式

$$ans = \sqrt[c]{v_1\times v_2\times ...\times v_k} = (v_1\times v_2\times ...\times v_k)^{\frac{1}{c}}$$

两边同时取以一个实数 $a$ 为底的对数,得到

$$\log_a{ans} = \log_a{(v_1\times v_2\times ...\times v_k)^{\frac{1}{c}}}$$

$$\log_a{ans} = \frac{1}{c}\times \log_a{(v_1\times v_2\times ...\times v_k)}$$

$$\log_a{ans} = \frac{1}{c}(\log_a{v_1}+\log_a{v_2}+...+\log_a{v_k})$$

因为这题只需要你求方案,所以你只要确保不同方案之间的相对魔力值即可,不用维护具体的 $ans$ 值,所以可以把 $ans$ 取 $log$,$log$ 的底数 $a$ 也可以随便取,大部分人应该都取的是自然对数 $e$

 

不难发现等号右边变成了一个类似于平均数的东西,仔细观察即可发现,把所有匹配串的魔力值 $v_i$ 取 $ln$ 后,你要使出现的所有匹配串的 $v_i$ 的平均数最大。

平均数最大这种东西就是套路的01分数规划……

具体做法就是,二分平均数 $x$,然后把所有匹配串的 $a_i$ 都减去 $x$,问题就变成了如何使 $v_i$ 之和最大。在所有模板串组成的 AC 自动机上 $dp$ 即可。

AC 自动机上 $dp$ 的状态就是 $f_{i,j}$ 表示确定模板串的前 $i$ 位,按模板串的前 $i$ 位跑 AC 自动机到达的点的编号为 $j$ 时,模板串的魔力值最大是多少。

然后判断一下模板串的第 $i$ 位是不是通配符就行了,是的话就可以往任意儿子转移,不是的话就要沿对应的字符边转移。

时间复杂度 $O(10ns\log{\frac{\ln v_{max}}{eps}})$。

这他吗什么复杂度,怎么跑过的……能过就行了

  1 #include<bits/stdc++.h>
  2 #define N 1505
  3 #define inf 1e99
  4 #define eps 1e-6
  5 using namespace std;
  6 inline int read(){
  7     int x=0; bool f=1; char c=getchar();
  8     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
  9     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
 10     if(f) return x;
 11     return 0;
 12 }
 13 int n,m;
 14 char T[N];
 15 namespace AC{
 16     int cnt,ch[N][12],sum[N]; double val[N];
 17     inline void ins(char *s,double v){
 18         int u=0,len=strlen(s),c;
 19         for(int i=0;i<len;++i){
 20             c=s[i]-'0';
 21             if(!ch[u][c]) ch[u][c]=++cnt;
 22             u=ch[u][c];
 23         }
 24         ++sum[u], val[u]+=v;
 25     }
 26     int que[N],l,r,fail[N];
 27     void BuildAC(){
 28         fail[0]=-1, que[l=r=1]=0;
 29         while(l<=r){
 30             int u=que[l++];
 31             for(int i=0;i<10;++i)
 32                 if(!ch[u][i]) ch[u][i]=ch[fail[u]][i];
 33                 else fail[ch[u][i]]=ch[fail[u]][i], que[++r]=ch[u][i];
 34         }
 35         for(int i=2;i<=r;++i){
 36             sum[que[i]]+=sum[fail[que[i]]];
 37             val[que[i]]+=val[fail[que[i]]];
 38             //cout<<que[i]<<' '<<fail[que[i]]<<' '<<sum[que[i]]<<' '<<val[que[i]]<<endl;
 39         }
 40     }
 41     
 42     double f[N][N]; int g[N][N][2]; char ansStr[N];
 43     double DP(double x){
 44         //cout<<x<<endl;
 45         for(int j=0;j<=cnt;++j) val[j]-=sum[j]*x;
 46         for(int i=0;i<=n;++i)
 47             for(int j=0;j<=cnt;++j)
 48                 f[i][j]=-inf;
 49         f[0][0]=0;
 50         for(int i=0;i<n;++i){
 51             for(int j=0;j<=cnt;++j){
 52                 if(f[i][j]==-inf) continue;
 53                 if(T[i]=='.'){
 54                     for(int k=0;k<10;++k){
 55                         int _j=ch[j][k];
 56                         if(f[i+1][_j]<f[i][j]+val[_j]){
 57                             f[i+1][_j]=f[i][j]+val[_j];
 58                         }
 59                     }
 60                 }
 61                 else{
 62                     int k=T[i]-'0', _j=ch[j][k];
 63                     if(f[i+1][_j]<f[i][j]+val[_j]) f[i+1][_j]=f[i][j]+val[_j];
 64                 }
 65             }
 66         }
 67         for(int i=0;i<=cnt;++i) val[i]+=sum[i]*x;
 68         int ans=0;
 69         for(int j=1;j<=cnt;++j) if(f[n][j]>f[n][ans]) ans=j;
 70         //cout<<f[n][ans]<<endl;
 71         return f[n][ans];
 72     }
 73     void _DP(double x){
 74         for(int j=0;j<=cnt;++j) val[j]-=sum[j]*x;
 75         for(int i=0;i<=n;++i)
 76             for(int j=0;j<=cnt;++j)
 77                 f[i][j]=-inf;
 78         f[0][0]=0;
 79         for(int i=0;i<n;++i){
 80             for(int j=0;j<=cnt;++j){
 81                 if(f[i][j]==-inf) continue;
 82                 if(T[i]=='.'){
 83                     for(int k=0;k<10;++k){
 84                         int _j=ch[j][k];
 85                         if(f[i+1][_j]<f[i][j]+val[_j]){
 86                             f[i+1][_j]=f[i][j]+val[_j],
 87                             g[i+1][_j][0]=j, g[i+1][_j][1]=k;
 88                         }
 89                     }
 90                 }
 91                 else{
 92                     int k=T[i]-'0', _j=ch[j][k];
 93                     if(f[i+1][_j]<f[i][j]+val[_j])
 94                         f[i+1][_j]=f[i][j]+val[_j],
 95                         g[i+1][_j][0]=j, g[i+1][_j][1]=k;
 96                 }
 97             }
 98         }
 99         for(int i=0;i<=cnt;++i) val[i]+=sum[i]*x;
100         int ans=0;
101         for(int j=1;j<=cnt;++j) if(f[n][j]>f[n][ans]) ans=j;
102         for(int i=n;i>0;--i){
103             ansStr[i-1]=g[i][ans][1]+'0';
104             ans=g[i][ans][0];
105         }
106     }
107 }
108 using namespace AC;
109 int main(){
110     //freopen("1.in","r",stdin);
111     //freopen("1.out","w",stdout);
112     n=read(), m=read(); scanf("%s",T);
113     char S[N]; double V;
114     for(int i=1;i<=m;++i){
115         scanf("%s%lf",S,&V);
116         ins(S,log(V));
117     }
118     BuildAC();
119     double l=0, r=log(1e9+1), mid, ans=0;
120     while(r-l>eps){
121         mid=(l+r)/2;
122         if(DP(mid)>0) ans=mid, l=mid;
123         else r=mid;
124     }
125     //cout<<ans<<endl;
126     _DP(ans);
127     printf("%s\n",ansStr);
128     return 0;
129 }
Viev Code

总结:

1. 这类题不能直接 $dp$ 求最大平均数。因为求最大平均数这种问题,除了分数规划外(即二分答案),只能在某些情况下用贪心(比如从大到小取)。

    若不能贪心,我们不能把上述 $dp$ 的值直接记为最大平均数 或者同时记一个最小的匹配数量。考虑平均数这个东西的本质,对于到达同一状态的两种情况,可能一种情况匹配的数少,平均数也更小;但把两种情况同时加入一个新数,这种情况的新平均数就可能比另一种情况的新平均数大了。比如两个数集 $\{10\}$ 和 $\{9,9,9,14\}$,前者的平均数是 $10$,后者的平均数是 $10.25$;但把两个数集同时加入一个数 $11$,前者的平均数变成了 $10.5$,后者的平均数变成了 $10.4$。所以如果用 $dp$ 求最大平均数,必须再开一维状态记匹配的串数(即要求多少个数的平均数),但匹配的串数可能很多,再开一维状态的话时空复杂度都不能承受。所以只能分数规划。

2. 这道题告诉我们一定要学好数学这门文化课,否则会被数学杀。

转载于:https://www.cnblogs.com/scx2015noip-as-php/p/bjoi2019d1t1.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/386814.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 篇基础知识10——多线程

1.线程的概念 单核CPU的计算机中&#xff0c;一个时刻只能执行一条指令&#xff0c;操作系统以“时间片轮转”的方式实现多个程序“同时”运行。操作系统以进程&#xff08;Process&#xff09;的方式运行应用程序&#xff0c;进程不但包括应用程序的指令流&#xff0c;也包括运…

快速理解binary cross entropy 二元交叉熵

Binary cross entropy 二元交叉熵是二分类问题中常用的一个Loss损失函数&#xff0c;在常见的机器学习模块中都有实现。本文就二元交叉熵这个损失函数的原理&#xff0c;简单地进行解释。 首先是二元交叉熵的公式 : Loss−1N∑i1Nyi⋅log⁡(p(yi))(1−yi)⋅log(1−p(yi))Loss …

Docker搭建自己的GitLab

Docker搭建自己的GitLab docker 介绍 **GitLab: ** GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的web服务 **Docker: ** Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖…

flowable 任务节点多实例使用

我们在使用Flowable 工作流引擎的时候&#xff0c;最常用的肯定是任务节点&#xff0c;因为在OA系统、审批系统、办公自动化系统中核心的处理就是流程的运转&#xff0c;在流程运转的时候&#xff0c;可能我们有这样的一个需求&#xff0c;在一个任务节点的时候&#xff0c;我们…

Linux的目录结构

Linux文件系统是呈树形结构&#xff0c;了解Linux文件系统的目录结构&#xff0c;对于我们驾驭Linux还是有必要的。 目录 说明 / Linux文件系统的入口&#xff0c;也是处于最高一级的目录 /bin 基本系统所需要的命令。功能和/usr/bin类似&#xff0c;这个目录中的文件都是…

一文看懂卷积神经网络CNN的核心

在之前&#xff0c;我总结了关于计算机神经网络与梯度下降的核心&#xff0c;详见下文链接 : 一文看懂计算机神经网络与梯度下降 本文主要会对图像相关的机器学习中最为重要的网络&#xff0c;卷积神经网络作个人的理解分析。 1. 为什么要使用卷积神经网络 在讲述原理之前&am…

[LeetCode] Two Sum

一刷&#xff1a; import java.util.Arrays;public class Solution1 { public int[] twoSum(int[] nums, int target) {int[] indexnew int[2];int sum0;for (int i 0; i < nums.length; i) {for (int j i1; j < nums.length; j) {sumnums[i]nums[j];index[0] i;index[…

机器学习理论梳理2 : KNN K近邻分类模型

本文主要梳理KNN&#xff0c;K近邻模型的基本原理。 从机器学习的大分类来看&#xff0c;K近邻模型属于监督学习中的一种判别式模型&#xff0c;常用于分类问题。初始的数据集中&#xff0c;包含了已经分类标签好的数据。一句话来说&#xff0c;K近邻模型就是通过计算实例与现…

docker安装配置gitlab详细过程

1、方法一 1 docker pull beginor/gitlab-ce:11.0.1-ce.0 2、方法二 如果服务器网路不好或者pull不下来镜像&#xff0c;只能在其它网路比较好的机器上pull下来镜像&#xff0c;导出成一个文件&#xff0c; 再下载上传到网路不好的机器上&#xff0c;然后再从文件中导出来&am…

集合对偶律:分别用图文证明

集合几个法则&#xff1a; 求证&#xff1a; 注&#xff1a;右上角C表示此集合的补集/余集 语言描述&#xff1a;A 并 B的补集 A的补集 交 B的补集 A交B的补集 A的补集 并 B的补集 文字证明&#xff1a;&#xff08;思路&#xff1a;证明两个集合相等&#xff0c;可证两集合…

keras实现嘴唇图像autoencoder

本文分享了我在silent speech 项目过程中实现的基于嘴唇图像数据集的autoencoder自编码器。输入输出都是64∗6464*6464∗64的嘴唇灰度图。自编码器由编码解码两个部分构成&#xff0c;同时实现了利用checkpoint在每个epoch运算时&#xff0c;自动保存测试集loss更小的模型。 数…

historyReverser array reverse

historyReverser & array reverse "use strict";/**** author xgqfrms* license MIT* copyright xgqfrms** description historyReverser* augments Reverse 逆向 / Recursive 递归* example* link**/const historyReverser (datas [], text , debug false)…

pip国内加载速度慢解决方法

在国内使用pip安装包时有时会发现安装速度非常慢&#xff0c;甚至连接不上源。 为了加快pip的下载速度&#xff0c;我们可以主动使用 -i命令来切换到国内源。 下面放出实测好用的国内源 : 清华&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple 阿里云&#xff1a;http:…

oracle--导出、导入blob类型的字段

oracle--导出、导入blob类型的字段 blob是oracle中的一个数据类型&#xff0c;保存的是压缩后的二进制形式的大数据。 数据迁移如果涉及到blob字段&#xff0c;都不好处理&#xff0c;因为无法用常规方法进行操作&#xff0c;如&#xff1a;使用select查看该字段&#xff0c;…

深度学习分布式训练小结

分布式训练本质上是为了加快模型的训练速度&#xff0c;面对较为复杂的深度学习模型以及大量的数据。单机单GPU很难在有限的时间内达成模型的收敛。这时候就需要用到分布式训练。 分布式训练又分为模型并行和数据并行两大类。 1. 数据并行 数据并行在于将不同batch的数据分别…

MAC配置JCO,与找不到sapjco3异常

①到jco官网下载jco压缩包&#xff0c;解压 ②把libsapjco3.jnilib 放到一个文件夹中 把该路径配置到环境变量中 ③项目运行有可能会出现异常&#xff1a;找不到 sapjco3 &#xff1b; 第一种解决方式&#xff1a;配置虚拟机参数&#xff1a;-Djava.library.path之前环境变量路…

Java高并发之BlockingQueue

前言碎语 当系统流量负载比较高时&#xff0c;业务日志的写入操作也要纳入系统性能考量之内&#xff0c;如若处理不当&#xff0c;将影响系统的正常业务操作&#xff0c;之前写过一篇《spring boot通过MQ消费log4j2的日志》的博文&#xff0c;采用了RabbitMQ消息中间件来存储抗…

IP通信基础回顾2(第三周)

1.TCP报文 序号字段占4个字节。TCP连接中传送的数据流中每一个字节都编上一个序号。序号字段的值则是本报文段所发送的数据第一个字节的序号。 确认序号占4个字节。是期望收到的对方的下一个报文段字节胡序号。首部长度占4个字节。指出TCP首部长度在20-60字节之间&#xff0c;所…

ThreadPoolExecutor线程池 + Queue队列

1&#xff1a;BlockingQueue继承关系 java.util.concurrent 包里的 BlockingQueue是一个接口&#xff0c; 继承Queue接口&#xff0c;Queue接口继承 Collection BlockingQueue----->Queue-->Collection 图&#xff1a; 队列的特点是&#xff1a;先进先出&#xff08;FIFO…

linux基础文件管理软硬链接

一、文件系统的基本结构 1、文件和目录被组成一个单根倒置树目录结构 2、文件系统从根目录下开始&#xff0c;用“/”表示 3、根文件系统&#xff08;rootfs&#xff09;&#xff1a;root filesystem文件名区分大小写 4、以 . 开头的文件为隐藏文件 5、路径用/隔离 6文件有两类…