【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}})$。
这他吗什么复杂度,怎么跑过的……能过就行了
#include<bits/stdc++.h>
#define N 1505
#define inf 1e99
#define eps 1e-6
using namespace std;
inline int read(){
int x=; bool f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=;
for(; isdigit(c);c=getchar()) x=(x<<)+(x<<)+(c^'');
if(f) return x;
return ;
}
int n,m;
char T[N];
namespace AC{
int cnt,ch[N][],sum[N]; double val[N];
inline void ins(char *s,double v){
int u=,len=strlen(s),c;
for(int i=;i<len;++i){
c=s[i]-'';
if(!ch[u][c]) ch[u][c]=++cnt;
u=ch[u][c];
}
++sum[u], val[u]+=v;
}
int que[N],l,r,fail[N];
void BuildAC(){
fail[]=-, que[l=r=]=;
while(l<=r){
int u=que[l++];
for(int i=;i<;++i)
if(!ch[u][i]) ch[u][i]=ch[fail[u]][i];
else fail[ch[u][i]]=ch[fail[u]][i], que[++r]=ch[u][i];
}
for(int i=;i<=r;++i){
sum[que[i]]+=sum[fail[que[i]]];
val[que[i]]+=val[fail[que[i]]];
//cout<<que[i]<<' '<<fail[que[i]]<<' '<<sum[que[i]]<<' '<<val[que[i]]<<endl;
}
} double f[N][N]; int g[N][N][]; char ansStr[N];
double DP(double x){
//cout<<x<<endl;
for(int j=;j<=cnt;++j) val[j]-=sum[j]*x;
for(int i=;i<=n;++i)
for(int j=;j<=cnt;++j)
f[i][j]=-inf;
f[][]=;
for(int i=;i<n;++i){
for(int j=;j<=cnt;++j){
if(f[i][j]==-inf) continue;
if(T[i]=='.'){
for(int k=;k<;++k){
int _j=ch[j][k];
if(f[i+][_j]<f[i][j]+val[_j]){
f[i+][_j]=f[i][j]+val[_j];
}
}
}
else{
int k=T[i]-'', _j=ch[j][k];
if(f[i+][_j]<f[i][j]+val[_j]) f[i+][_j]=f[i][j]+val[_j];
}
}
}
for(int i=;i<=cnt;++i) val[i]+=sum[i]*x;
int ans=;
for(int j=;j<=cnt;++j) if(f[n][j]>f[n][ans]) ans=j;
//cout<<f[n][ans]<<endl;
return f[n][ans];
}
void _DP(double x){
for(int j=;j<=cnt;++j) val[j]-=sum[j]*x;
for(int i=;i<=n;++i)
for(int j=;j<=cnt;++j)
f[i][j]=-inf;
f[][]=;
for(int i=;i<n;++i){
for(int j=;j<=cnt;++j){
if(f[i][j]==-inf) continue;
if(T[i]=='.'){
for(int k=;k<;++k){
int _j=ch[j][k];
if(f[i+][_j]<f[i][j]+val[_j]){
f[i+][_j]=f[i][j]+val[_j],
g[i+][_j][]=j, g[i+][_j][]=k;
}
}
}
else{
int k=T[i]-'', _j=ch[j][k];
if(f[i+][_j]<f[i][j]+val[_j])
f[i+][_j]=f[i][j]+val[_j],
g[i+][_j][]=j, g[i+][_j][]=k;
}
}
}
for(int i=;i<=cnt;++i) val[i]+=sum[i]*x;
int ans=;
for(int j=;j<=cnt;++j) if(f[n][j]>f[n][ans]) ans=j;
for(int i=n;i>;--i){
ansStr[i-]=g[i][ans][]+'';
ans=g[i][ans][];
}
}
}
using namespace AC;
int main(){
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
n=read(), m=read(); scanf("%s",T);
char S[N]; double V;
for(int i=;i<=m;++i){
scanf("%s%lf",S,&V);
ins(S,log(V));
}
BuildAC();
double l=, r=log(1e9+), mid, ans=;
while(r-l>eps){
mid=(l+r)/;
if(DP(mid)>) ans=mid, l=mid;
else r=mid;
}
//cout<<ans<<endl;
_DP(ans);
printf("%s\n",ansStr);
return ;
}
Viev Code
总结:
1. 这类题不能直接 $dp$ 求最大平均数。因为求最大平均数这种问题,除了分数规划外(即二分答案),只能在某些情况下用贪心(比如从大到小取)。
若不能贪心,我们不能把上述 $dp$ 的值直接记为最大平均数 或者同时记一个最小的匹配数量。考虑平均数这个东西的本质,对于到达同一状态的两种情况,可能一种情况匹配的数少,平均数也更小;但把两种情况同时加入一个新数,这种情况的新平均数就可能比另一种情况的新平均数大了。比如两个数集 $\{10\}$ 和 $\{9,9,9,14\}$,前者的平均数是 $10$,后者的平均数是 $10.25$;但把两个数集同时加入一个数 $11$,前者的平均数变成了 $10.5$,后者的平均数变成了 $10.4$。所以如果用 $dp$ 求最大平均数,必须再开一维状态记匹配的串数(即要求多少个数的平均数),但匹配的串数可能很多,再开一维状态的话时空复杂度都不能承受。所以只能分数规划。
2. 这道题告诉我们一定要学好数学这门文化课,否则会被数学杀。
【BJOI 2019】奥术神杖的更多相关文章
- Loj #3089. 「BJOI2019」奥术神杖
Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...
- [BJOI2019]奥术神杖(分数规划,动态规划,AC自动机)
[BJOI2019]奥术神杖(分数规划,动态规划,AC自动机) 题面 洛谷 题解 首先乘法取\(log\)变加法,开\(c\)次根变成除\(c\). 于是问题等价于最大化\(\displaystyle ...
- [BJOI2019]奥术神杖——AC自动机+DP+分数规划+二分答案
题目链接: [BJOI2019]奥术神杖 答案是$ans=\sqrt[c]{\prod_{i=1}^{c}v_{i}}=(\prod_{i=1}^{c}v_{i})^{\frac{1}{c}}$. 这 ...
- 【LOJ】#3089. 「BJOI2019」奥术神杖
LOJ#3089. 「BJOI2019」奥术神杖 看见乘积就取log,开根号就是除法,很容易发现这就是一道01分数规划.. 然后建出AC自动机直接dp就行,判断条件要设成>0,因为起点的值是1, ...
- luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP)
luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP) Luogu 题解时间 难点在于式子转化,设有c个满足的子串,即求最大的 $ ans = \sqrt[c]{\prod_{ ...
- 【题解】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]奥术神杖
传送门 要求的东西带个根号,这玩意叫几何平均数,说到平均数,我们就能想到算术平均数(就是一般意义下的平均数),而这个东西是一堆数之积开根号,所以如果每个数取对数,那么乘法会变成加法,开根号变成除法,所 ...
随机推荐
- Activiti学习记录(四)
1 连线 注意:如果将流程图放置在和java类相同的路径,需要配置: 1.1 部署流程定义+启动流程实例 ProcessEngine processEngine = ProcessEngines.ge ...
- 关于SQL数据库 msdb.dbo.sp_send_dbmail 函数发送邮件的场景分析
关于SQL数据库 msdb.dbo.sp_send_dbmail 函数发送邮件的场景分析 在推行系统中,时不时会有用户提出希望系统能自动推送邮件,由于手头的工具和能力有限,不少需求都借助于sql se ...
- 2019 ACM-ICPC全国邀请赛(西安) M.Travel 二分+判联通
https://nanti.jisuanke.com/t/39280 讲道理这题写bfs求最大边权限制下从1到n的最短步数,然后二分判一下就行了. 然鹅我还是直接套了dij,一开始纠结dij能不能过, ...
- 微信小游戏 demo 飞机大战 代码分析 (三)(spirit.js, animation.js)
微信小游戏 demo 飞机大战 代码分析(三)(spirit.js, animation.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞机大战 代码 ...
- javascript的基本类型和引用类型
一.基本类型和引用类型 基本的数据类型有5个:undefined,boolean,number,string,null ? 1 2 3 4 5 typeof null; //"object& ...
- 【jenkins】jenkins服务器与svn服务器时间不一致出现的问题
问题描述: svn提交了一次更新包,到了jenkins提交更新的时候,第一次代码没有生效,然后重新提交了一次,第二次才生效. 问题排查: 1.首先第一反应比对了下两次更新的包文件是否一致,然后发现大小 ...
- 致敬wusir懒孩子自有懒孩子的生存之道之二
https://www.cnblogs.com/wupeiqi/ https://www.cnblogs.com/Eva-J/ https://www.cnblogs.com/wupeiqi/p/90 ...
- 802. Find Eventual Safe States
https://leetcode.com/problems/find-eventual-safe-states/description/ class Solution { public: vector ...
- JVM垃圾回收--年轻代、年老点和持久代(转)
关键字约定 Young generation –>新生代 Tenured / Old Generation –>老年代 Perm Area –>永久代 年轻代: 所有新生成的对象 ...
- python数据模型(特殊方法)
python中的全部特殊方法 本部分内容可以参考官方网址 python中一共有83个特殊方法,其中47个用于算术运算.位运算和比较操作.我根据<流畅的python>中的整理,摘录如下两个表 ...