卡精度好题

最关键的一步是几何平均数的\(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]奥术神杖的更多相关文章

  1. [BJOI2019]奥术神杖(分数规划,动态规划,AC自动机)

    [BJOI2019]奥术神杖(分数规划,动态规划,AC自动机) 题面 洛谷 题解 首先乘法取\(log\)变加法,开\(c\)次根变成除\(c\). 于是问题等价于最大化\(\displaystyle ...

  2. [BJOI2019]奥术神杖——AC自动机+DP+分数规划+二分答案

    题目链接: [BJOI2019]奥术神杖 答案是$ans=\sqrt[c]{\prod_{i=1}^{c}v_{i}}=(\prod_{i=1}^{c}v_{i})^{\frac{1}{c}}$. 这 ...

  3. luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP)

    luoguP5319 [BJOI2019]奥术神杖(分数规划,AC自动机DP) Luogu 题解时间 难点在于式子转化,设有c个满足的子串,即求最大的 $ ans = \sqrt[c]{\prod_{ ...

  4. 【题解】Luogu P5319 [BJOI2019]奥术神杖

    原题传送门 题目让我们最大化\(val=\sqrt[k]{\prod_{i=1}^k w_i}\),其中\(k\)是咒语的个数,\(w_i\)是第\(i\)个咒语的神力 看着根号和累乘不爽,我们两边同 ...

  5. [BJOI2019]奥术神杖

    https://www.luogu.org/problemnew/show/P5319 题解 首先观察我们要求的答案的形式: \[ \biggl(\prod V_i \biggr)^x\ \ \ x= ...

  6. [BJOI2019]奥术神杖(分数规划+AC自动机+DP)

    题解:很显然可以对权值取对数,然后把几何平均值转为算术平均值,然后很显然是分数规划.先对每个模式串建立AC自动机,每个节点w[i],sz[i]分别表示以其为前缀的字符串,然后再二分最优解k,然后w[i ...

  7. luogu P5319 [BJOI2019]奥术神杖

    传送门 要求的东西带个根号,这玩意叫几何平均数,说到平均数,我们就能想到算术平均数(就是一般意义下的平均数),而这个东西是一堆数之积开根号,所以如果每个数取对数,那么乘法会变成加法,开根号变成除法,所 ...

  8. [BJOI2019]奥术神杖(AC自动机,DP,分数规划)

    题目大意: 给出一个长度 $n$ 的字符串 $T$,只由数字和点组成.你可以把每个点替换成一个任意的数字.再给出 $m$ 个数字串 $S_i$,第 $i$ 个权值为 $t_i$. 对于一个替换方案,这 ...

  9. [BJOI2019] 奥术神杖 [取log+AC自动机+dp]

    题面 传送门 思路 首先,看到这个乘起来开根号的形式,应该能想到用取$\log$的方式做一个转化: $\sqrt[n]{\prod_i a_i}=\frac{1}{n}\sum_i \log_b a_ ...

随机推荐

  1. jQuery之JSP加载JS文件不起作用的有效解决方法

    JSP加载JS文件不起作用的有效解决方法 作者: 字体:[增加 减小] 类型:转载 时间:2014-04-08 jsp导入jquery文件,老是不起作用,原因在于其不能访问/WEB-INF/目录下的文 ...

  2. Windows 8风格应用-触控输入

    参考:演练:创建您的第一个触控应用程序 http://msdn.microsoft.com/zh-cn/library/ee649090(v=vs.110).aspx win8支持多点触摸技术,而我们 ...

  3. 【NIFI】 实现数据库到数据库之间数据同步

    本里需要基础知识:[NIFI] Apache NiFI 安装及简单的使用 数据同步 界面如下: 具体流程: 1.使用ExecuteSQL连接mysql数据库,通过写sql查询所需要的数据 2.nifi ...

  4. python入门之深浅copy

    a1=["a","b","c","aa"] b1=a1 a1[0]=" print(a1,b1) 此时结果为: ...

  5. unity延时函数

    新建一个工具类 public class DelayToInvoke : MonoBehaviour{ public static IEnumerator DelayToInvokeDo(Action ...

  6. Linux 第三天

    2.文件处理命令 1)touch 创建空文件 语法:touch文件名 2)cat 显示文件内容 英文原意:concatenate 语法:cat 文件名 常用选项: -n:number,显示行号 3)t ...

  7. TCP粘包问题分析和解决(全)

    TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...

  8. C++STL 函数对象和谓词

    函数对象:重载函数调用操作符的类,其对象常称为函数对象. 函数对象属于类对象,能突破函数概念,保持类的状态 谓词: 一元函数对象:函数参数1个: 二元函数对象:函数参数2个: 一元谓词 函数参数1个, ...

  9. 深入理解,函数声明、函数表达式、匿名函数、立即执行函数、window.onload的区别.

    一.函数声明.函数表达式.匿名函数1.函数声明:function fnName () {…};使用function关键字声明一个函数,再指定一个函数名,叫函数声明. 2.函数表达式 var fnNam ...

  10. SDIBT 2345 (3.2.1 Factorials 阶乘)

    Description N的阶乘写作N!表示小于等于N的所有正整数的乘积.阶乘会很快的变大,如13!就必须用32位整数类型来存储,70!即使用浮点数也存不下了.你的任务是找到阶乘最后面的非零位.举个例 ...