[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 ...
随机推荐
- List集合分组实现教程
封装一个方法,用一个Map来实现,这里是根据bean类的seq字段进行拆分的,分成好几个list private LinkedHashMap<String,List<HandleInfo& ...
- 明解C语言 入门篇 第十三章答案
练习13-1 /* 打开与关闭文件 */ #include <stdio.h> int main(void) { ]; FILE* fp; printf("请输入你要打开的文件& ...
- 这个meta标签会让华为mate10 pro自带浏览器无法粘贴手机收到的验证码信息
前言 最近在项目中遇到一个问题,注册登录界面点击获取验证码,手机收到短信验证码后可以复制成功,但无法粘贴 让人郁闷的是在其它上手机上的(比如小米,苹果)默认浏览器和其它手机浏览器(比如QQ,夸克,搜 ...
- Elasticsearch搜索调优权威指南 (1/3)
本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/qwkZKLb_ghmlwrqMkqlb7Q英文原文:https://qbox.io/blog/ela ...
- python asyncio call_soon, call_at, call_later
1. call_soon, 协程一运行就马上运行 def callback(sleep_times): print("success time {}".format(sleep_t ...
- LibLog 类库 分析
前期思考: Microsoft.Logging 是否可用? 是否需要提供默认的 Logger 实现? 不需要.1,用户自己开启日志功能,设置开启属性,即可打印出相应的日志. LibLog 类库分析: ...
- WPF-如何添加用户控件(同一个程序集与非同一个程序集)
在WPF中,假如十个按钮与十个文本框需要在窗体中多次使用,每次都都要重新添加这二十个按钮,显然是不可取的.这时,可以把这二十个按钮封装成一个UserControl,然后多次引用. 一.新建一个用户控件 ...
- C# 改变控制台背景颜色
之前查找静态构造函数相关的问题无意间碰到的一个问题.改变控制台的背景颜色. static void Main(string[] args) { //设置绿色 Console.BackgroundCol ...
- codeforces #592(Div.2)
codeforces #592(Div.2) A Pens and Pencils Tomorrow is a difficult day for Polycarp: he has to attend ...
- nodejs块级作用域
现在让我们了解3个关键字var.let.const,的特性和使用方法. var JavaScript中,我们通常说的作用域是函数作用域,使用var声明的变量,无论是在代码的哪个地方声明的,都会提升到当 ...