#loj3089 [BJOI2019]奥术神杖
卡精度好题
最关键的一步是几何平均数的\(ln\)等于所有数字取\(ln\)后的算术平均值
那么现在就变成了一个很裸的01分数规划问题,一个通用的思路就是二分答案
现在来考虑二分答案的底层怎么写
把所有串拉出来造ac自动机,那么ac自动机上一个点的权值就是
fail树上这个点到祖先的树链上的字符串的权值之和
那么接下来设\(f(i,j)\)表示决策到了第\(i\)个字符,走到自动机节点\(j\)的最大收益大力dp即可
由于我们不希望均值是0,因此额外记录下有没有匹配上模式串即可
二分完了之后把mid调成l重新跑一遍dp,不然你会跑出无解的情况导致输出错误的答案
有个函数叫log2,精度比log高,这样你就可以少二分几次了
// luogu-judger-enable-o2
// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;const int N=4000;typedef long long ll;
typedef long double db;db mid;//const db eps=1e-8;
const db low_inf=-1e9;
int v[N<<1];int x[N<<1];int ct;int al[N];
db we[N];db sum[N];int num[N];int snum[N];
inline void add(int u,int V){
//printf("add %d %d\n",u,V);
v[++ct]=V;x[ct]=al[u];al[u]=ct;}
inline void pdfs(int u)
{
snum[u]+=num[u];
for(int i=al[u];i;i=x[i])
snum[v[i]]=snum[u],pdfs(v[i]);
}
inline void dfs(int u)
{
sum[u]+=we[u]-mid*num[u];
for(int i=al[u];i;i=x[i])
sum[v[i]]=sum[u],dfs(v[i]);
}
struct trie
{
int mp[N][12];int cnt;int fil[N];
inline int ins(int p,int c)
{return mp[p][c]=(mp[p][c])?mp[p][c]:++cnt;}
inline void build()
{
queue <int> q;
for(int i=1;i<=10;i++)
if(mp[1][i])fil[mp[1][i]]=1,q.push(mp[1][i]);
else mp[1][i]=1;
while(!q.empty())
{
int nw=q.front();q.pop();
for(int i=1;i<=10;i++)
if(mp[nw][i])fil[mp[nw][i]]=mp[fil[nw]][i],q.push(mp[nw][i]);
else mp[nw][i]=mp[fil[nw]][i];
}
for(int i=1;i<=cnt;i++)
if(fil[i])add(fil[i],i);
}
}tr;
struct data{int c;int lst;int pval;}fr[N][N];db dp[N][N];
char mde[N];int n;int m;char smde[N];int op[N];int hd;
inline void trans(int i,int j,int k)
{
int tw=tr.mp[j][k];
db tval=dp[i][j]+sum[tw];
if(tval>=dp[i+1][tw])
{
dp[i+1][tw]=tval;
fr[i+1][tw]=(data){k,j,fr[i][j].pval+snum[tw]};
}
}
inline void pritans()
{
db curmx=-0x3f3f3f3f;int st=-1;
for(int i=1;i<=tr.cnt;i++)
if(curmx<dp[n][i])
curmx=dp[n][i],st=i;
hd=0;
for(int i=n;i>=1;i--)
op[++hd]=fr[i][st].c,st=fr[i][st].lst;
for(int i=n;i>=1;i--)
printf("%d",op[i]-1);
printf("\n");
}
inline bool jud()
{
//for(int i=1;i<=tr.cnt;i++)
// printf("%.3Lf ",sum[i]);printf("\n");
sum[1]=0;
dfs(1);
for(int i=0;i<=n;i++)
for(int j=1;j<=tr.cnt;j++)
dp[i][j]=-0x3f3f3f3f;
dp[0][1]=0;
for(int i=0;i<n;i++)
for(int j=1;j<=tr.cnt;j++)
{
if(dp[i][j]<low_inf)continue;
if(mde[i+1]=='.')
for(int k=1;k<=10;k++)
trans(i,j,k);
else
trans(i,j,mde[i+1]-'0'+1);
}
db mx=-0x3f3f3f3f;
for(int i=1;i<=tr.cnt;i++)
if(fr[n][i].pval)mx=max(mx,dp[n][i]);
//printf("mx=%.10Lf\n",mx);
// pritans();
return mx>=0;
}
int main()
{
//printf("%.10lf\n",exp(log(10)));
scanf("%d%d",&n,&m);
scanf("%s",mde+1);
tr.cnt=1;
for(int i=1,tmp;i<=m;i++)
{
scanf("%s",smde+1);
int p=1;
for(int j=1;smde[j]!='\0';j++)
p=tr.ins(p,smde[j]-'0'+1);
scanf("%d",&tmp);
we[p]+=log2(tmp);num[p]++;
// printf("%.10Lf\n",we[p]);
}
tr.build();
pdfs(1);
db l=0;db r=log2(1e9);
for(int i=1;i<=18;i++)
{
// printf("%.10Lf %.10Lf\n",l,r);
mid=(l+r)/2;
if(jud())l=mid;else r=mid;
}
mid=l;
jud();
pritans();
return 0;
}
#loj3089 [BJOI2019]奥术神杖的更多相关文章
- [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}}$. 这 ...
- 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]奥术神杖
传送门 要求的东西带个根号,这玩意叫几何平均数,说到平均数,我们就能想到算术平均数(就是一般意义下的平均数),而这个东西是一堆数之积开根号,所以如果每个数取对数,那么乘法会变成加法,开根号变成除法,所 ...
- [BJOI2019]奥术神杖(AC自动机,DP,分数规划)
题目大意: 给出一个长度 $n$ 的字符串 $T$,只由数字和点组成.你可以把每个点替换成一个任意的数字.再给出 $m$ 个数字串 $S_i$,第 $i$ 个权值为 $t_i$. 对于一个替换方案,这 ...
- [BJOI2019] 奥术神杖 [取log+AC自动机+dp]
题面 传送门 思路 首先,看到这个乘起来开根号的形式,应该能想到用取$\log$的方式做一个转化: $\sqrt[n]{\prod_i a_i}=\frac{1}{n}\sum_i \log_b a_ ...
随机推荐
- java通过IP地址获取物理位置
import java.io.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern ...
- hdu6069 多校Counting Divisors
思路:对于n^k其实就是每个因子的个数乘了一个K.然后现在就变成了求每个数的每个质因子有多少个,但是比赛的时候只想到sqrt(n)的分解方法,总复杂度爆炸,就一直没过去,然后赛后看官方题解感觉好妙啊! ...
- Python多进程并发操作进程池Pool
目录: multiprocessing模块 Pool类 apply apply_async map close terminate join 进程实例 multiprocessing模块 如果你打算编 ...
- ubunut下安装ibus_pinyin中文输入法
ubuntu安装中文输入法,,此处一ibus-pinyin为例为其安装中文输入法,,, 1. 设置(setting)---语言支持(language support)---汉语(chinese),,, ...
- AOP 环绕通知 (Schema-base方式) 和 AspectJ方式在通知中获取切点的参数
环绕通知(Schema- base方式) 1.把前置通知和后置通知都写到一个通知中,组成了环绕通知 2.实现步骤: 2.1 新建一个类实现 MethodInterceptor 接口 public cl ...
- flask框架基础
一 web的一些框架介绍 Flask:短小精悍,内部没有包含多少组件,但是第三方的组件是非常丰富的. Django:django是一个重武器,内部包含了非常多的组件:orm,form,modelFor ...
- RAID : 独立磁盘冗余阵列(Redundant Array of Independent Disks)
RAID 分为不用的等级(RAID0 - RAID5),以满足不同的数据应用需求. RAID 是由多个独立的高性能磁盘驱动器组成的磁盘子系统,从而提供比单个磁盘更高的存储性能和数据冗余的技术. AID ...
- spring boot 实现mybatis拦截器
spring boot 实现mybatis拦截器 项目是个报表系统,服务端是简单的Java web架构,直接在请求参数里面加了个query id参数,就是mybatis mapper的query id ...
- lambda表达式(c++11)
1.概念 1)lambda表达式是一个可调用的代码单元,它由一个捕获列表.一个参数列表.一个箭头.一个返回类型.一个函数体组成: 2)可以忽略参数列表和返回类型,但必须包含捕获列表和函数体: 3)忽略 ...
- .net 打开Excel文档并转为DataTable
/// <summary> /// 打开Excel文档并转为DataTable /// </summary> /// <returns></returns&g ...