[BJOI2019]奥术神杖(AC自动机,DP,分数规划)
题目大意:
给出一个长度 $n$ 的字符串 $T$,只由数字和点组成。你可以把每个点替换成一个任意的数字。再给出 $m$ 个数字串 $S_i$,第 $i$ 个权值为 $t_i$。
对于一个替换方案,这样定义它的价值:
如果数字串 $S_i$ 在 $T$ 中出现了,那么将 $t_i$ 加入多重集。如果出现多次也要加多次。
它的价值就是这个多重集元素的几何平均数(所有 $c$ 个数的乘积开 $c$ 次方根)。
请构造出一个替换方案,使得这个值最大。不用输出这个价值。
$1\le n\le 1500,1\le \sum|S_i|\le 1500,1\le t_i\le 10^9$。
首先这个奇怪的式子是乘积的形式。如果将它取对数:(其实不一定要用 $\ln$,任意底数都可以)
$$\dfrac{1}{c}\sum\limits_{i=1}^c\ln x_i$$
$$\dfrac{\sum\limits_{i=1}^c\ln x_i}{\sum\limits_{i=1}^c 1}$$
这就是分数规划经典模式了。
二分 $x$,看看这个式子的值能不能 $>v$。可以就缩小左边界,否则缩小右边界。
$$\dfrac{1}{c}\sum\limits_{i=1}^c\ln x_i>v$$
$$\sum\limits_{i=1}^c\ln x_i>vc$$
$$\sum\limits_{i=1}^c(\ln x_i-v)>0$$
设第 $i$ 个串的新权值 $w_i=\ln x_i-v$,那么就是要找出一个方案使得 $w_i$ 之和最大,看看是否 $>0$ 即可。
如何求最大值?实际上是个套路 DP 了。
先建出所有 $S_i$ 串的 AC 自动机。然后设 $wsum_u$ 为从 $u$ 开始跳 fail 指针,跳过的所有点的 $w_i$ 之和。要记录这个是因为在 AC 自动机上走到点 $u$ 时,沿着 $u$ 跳 fail 能跳到的所有点都是可以匹配的。
令 $f[i][j]$ 表示按照 $T$ 串走了 $i$ 步,走到点 $j$ 的最大值。
可以从 $f[i][j]+wsum_c$ 转移到 $f[i+1][c]$。其中 $T_{i+1}$ 已经确定时 $c$ 就是对应的儿子,否则就要枚举替换成什么字符,再转移到相应的儿子。
由于要输出方案,要记录从哪个状态转移过来。
时间复杂度 $O(n\sum|S_i|\log)$。
#include<bits/stdc++.h>
using namespace std;
const int maxn=;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
char ch=getchar();int x=,f=;
while(ch<'' || ch>'') f|=ch=='-',ch=getchar();
while(ch>='' && ch<='') x=x*+ch-'',ch=getchar();
return f?-x:x;
}
int n,m,nd,ch[maxn][],fail[maxn],cnt[maxn],q[maxn],h,r,fr[maxn][maxn],id[maxn][maxn];
double val[maxn],hhh[maxn],dp[maxn][maxn];
char s[maxn],t[maxn],tmp[maxn];
void insert(const char *s,int x){
int now=,l=strlen(s+);
FOR(i,,l){
int p=s[i]-'';
if(!ch[now][p]) ch[now][p]=++nd;
now=ch[now][p];
}
val[now]+=log(x);
cnt[now]++;
}
void build(){
h=;r=;
FOR(i,,) if(ch[][i]) q[++r]=ch[][i];
while(h<=r){
int u=q[h++];
val[u]+=val[fail[u]];
cnt[u]+=cnt[fail[u]];
FOR(i,,) if(ch[u][i]) fail[q[++r]=ch[u][i]]=ch[fail[u]][i];
else ch[u][i]=ch[fail[u]][i];
}
}
void trans(int i,int j,int k){
int c=ch[j][k];
if(dp[i][j]+hhh[c]>dp[i+][c]){
dp[i+][c]=dp[i][j]+hhh[c];
fr[i+][c]=j;
id[i+][c]=k;
}
}
bool check(double x){
FOR(i,,nd) hhh[i]=val[i]-cnt[i]*x;
FOR(i,,n) FOR(j,,nd) dp[i][j]=-1e9;
dp[][]=;
FOR(i,,n-) FOR(j,,nd){
if(s[i+]=='.') FOR(k,,) trans(i,j,k);
else trans(i,j,s[i+]-'');
}
int mxid=;
FOR(i,,nd) if(dp[n][i]>dp[n][mxid]) mxid=i;
if(dp[n][mxid]<=) return false;
int at=mxid;
ROF(i,n,){
t[i]=id[i][at]+'';
at=fr[i][at];
}
return true;
}
int main(){
n=read();m=read();
scanf("%s",s+);
FOR(i,,m){
scanf("%s",tmp+);
insert(tmp,read());
}
build();
double l=,r=log(1e9);
while(r-l>1e-){
double mid=(l+r)/;
if(check(mid)) l=mid;
else r=mid;
}
check(l);
printf("%s\n",t+);
}
[BJOI2019]奥术神杖(AC自动机,DP,分数规划)的更多相关文章
- [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」奥术神杖——AC自动机DP+0/1分数规划
题目:https://loj.ac/problem/3089 没想到把根号之类的求对数变成算数平均值.写了个只能得15分的暴力. #include<cstdio> #include< ...
- P5319-[BJOI2019]奥术神杖【0/1分数规划,AC自动机,dp】
正题 题目链接:https://www.luogu.com.cn/problem/P5319 题目大意 一个长度为\(n\)的串\(T\),用\(0\sim 9\)填充所有的\(.\). 然后给出\( ...
- luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP)
luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP) Luogu 题解时间 难点在于式子转化,设有c个满足的子串,即求最大的 $ ans = \sqrt[c]{\prod_{ ...
- [BJOI2019]奥术神杖(分数规划,动态规划,AC自动机)
[BJOI2019]奥术神杖(分数规划,动态规划,AC自动机) 题面 洛谷 题解 首先乘法取\(log\)变加法,开\(c\)次根变成除\(c\). 于是问题等价于最大化\(\displaystyle ...
- POJ1625 Censored!(AC自动机+DP)
题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后 ...
- HDU2296 Ring(AC自动机+DP)
题目是给几个带有价值的单词.而一个字符串的价值是 各单词在它里面出现次数*单词价值 的和,问长度不超过n的最大价值的字符串是什么? 依然是入门的AC自动机+DP题..不一样的是这题要输出具体方案,加个 ...
- HDU2457 DNA repair(AC自动机+DP)
题目一串DNA最少需要修改几个基因使其不包含一些致病DNA片段. 这道题应该是AC自动机+DP的入门题了,有POJ2778基础不难写出来. dp[i][j]表示原DNA前i位(在AC自动机上转移i步) ...
- hdu 4117 GRE Words AC自动机DP
题目:给出n个串,问最多能够选出多少个串,使得前面串是后面串的子串(按照输入顺序) 分析: 其实这题是这题SPOJ 7758. Growing Strings AC自动机DP的进阶版本,主题思想差不多 ...
- hdu 2457(ac自动机+dp)
题意:容易理解... 分析:这是一道比较简单的ac自动机+dp的题了,直接上代码. 代码实现: #include<stdio.h> #include<string.h> #in ...
随机推荐
- python 学习常见问题笔记
1.for...if...构建List segs = [v for v in segs if not str(v).isdigit()]#去数字 https://www.cnblogs.com/eni ...
- find命令常用场景
1.查找/var目录下属主为root并且属组为mail的所有文件: find /var -user root -group mail 2.查找/usr目录下不属于root,bin,或student的文 ...
- HBuilder webApp开发(七)微信/QQ/新浪/腾讯微博分享
链接 https://blog.csdn.net/zhuming3834/article/details/51706256
- Python自定义注解
Python3.0之后加入新特性Decorators,以@为标记修饰function和class.有点类似c++的宏和java的注解.Decorators用以修饰约束function和class,分为 ...
- 【转载】百度百科:FusionCube超融合
[转载]百度百科:FusionCube超融合 华为FusionCube融合基础设施一体机(Huawei FusionCube Converged Infrastructure)是华为公司IT产品线云计 ...
- 休谟:《人性论》一书中提出的要重视"是"与"应该"的区别
"价值"最初是经济学的范畴,指的是经济价值.商品价值.价值作的为一个哲学概念,首先大概是由18 世纪的英国哲学家休谟(David H ume,1711-1776)提出的.他于173 ...
- [Flutter] Windows桌面程序开发
在今年5月的谷歌I/O 2019大会时, 谷歌就宣布了flutter已经支持全平台开发, 包括 android, ios, mac, linux, windows, web 等 . Flutter桌面 ...
- B-Tree详解
之前写过一篇关于索引的文章<SQL夯实基础(五):索引的数据结构>,这次我们主要详细讨论下B-Tree. B-树 B-tree,即B树,而不要读成B减树,它是一种多路搜索树(并不是二叉的) ...
- 【转】测试开发工程师必备软硬能力&高级测试开发工程师需要具备什么能力?
对于测试的基本知识,可以查看软件测试相关书籍 对于在公司成为一位优秀的测试开发工程师,我觉得下面这篇文章涉及到的是我们需要的,稍微进行改动https://blog.csdn.net/sinat_210 ...
- 「黑客必备技能」Python正则表达式详解
说在前面 正则表达式是一个很强大的字符串处理工具,几乎任何关于字符串的操作都可以使用正则表达式来完成,作为一个爬虫工作者,每天和字符串打交道,正则表达式更是不可或缺的技能. 正则表达式在不同的语言中使 ...