题目:https://loj.ac/problem/3089

没想到把根号之类的求对数变成算数平均值。写了个只能得15分的暴力。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define db double
using namespace std;
const int N=,K=; const db eps=1e-;
int n,m,tot=,c[N][K],fl[N]; db ans;
int hd[N],xnt,to[N<<],nxt[N<<],vl[N],sm[N];
int l[N],v[N],dy[N],q[N]; db vp[N][N];
char s[N],ch[N],prn[N];
db pw(db x,int k)
{db ret=;while(k){if(k&)ret*=x;x*=x;k>>=;}return ret;}
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
int ins(int len)
{
int cr=;
for(int i=;i<=len;i++)
{
int w=ch[i]-'';
if(!c[cr][w])c[cr][w]=++tot;
cr=c[cr][w];
}
return cr;
}
void get_fl()
{
int he=,tl=;
for(int i=;i<;i++)
if(c[][i])q[++tl]=c[][i],fl[c[][i]]=;
else c[][i]=;
while(he<tl)
{
int k=q[++he],pr=fl[k];
for(int i=;i<;i++)
if(c[k][i])q[++tl]=c[k][i],fl[c[k][i]]=c[pr][i];
else c[k][i]=c[pr][i];
}
for(int i=;i<=tot;i++)add(fl[i],i);
}
void m_dfs(int cr)
{
sm[cr]=vl[cr];
for(int i=hd[cr],v;i;i=nxt[i])
m_dfs(v=to[i]), sm[cr]+=sm[v];
}
db fnd(int ct,db tp)
{
db l=,r=tp,ret=;
while(r-l>eps)
{
db mid=(l+r)/;
if(pw(mid,ct)<=tp)ret=mid,l=mid+eps;
else r=mid-eps;
}
return ret;
}
void dfs(int cr,int nw)
{
if(cr>n)
{
m_dfs(); db tp=; int ct=;
for(int i=;i<=m;i++)
{ tp*=vp[i][sm[dy[i]]]; ct+=sm[dy[i]];}
db d=fnd(ct,tp);
if(d>ans)
{ ans=d;for(int i=;i<=n;i++)prn[i]=s[i];}
return;
}
if(s[cr]!='.')
{
nw=c[nw][s[cr]-'']; vl[nw]++;
dfs(cr+,nw); vl[nw]--; return;
}
int ynw=nw;
for(int i=;i<;i++)
{
s[cr]=i+''; nw=c[ynw][i];
vl[nw]++; dfs(cr+,nw); vl[nw]--;
}
s[cr]='.';////
}
void solve1()
{
for(int i=;i<=m;i++)
{
vp[i][]=;//[0]!!
for(int j=,lm=n-l[i]+;j<=lm;j++)
vp[i][j]=vp[i][j-]*v[i];
}
dfs(,);
for(int i=;i<=n;i++)putchar(prn[i]); puts("");
}
namespace S2{
bool en[N],dp[N][N]; int pr[N][N],pw[N][N];
void dfs(int cr)
{
for(int i=hd[cr],v;i;i=nxt[i])
en[v=to[i]]|=en[cr], dfs(v);
}
void solve()
{
for(int i=;i<=m;i++)en[dy[i]]=;
m_dfs(); dp[][]=; int ni=,nj;
for(int i=;i<=n&&!ni;i++)
for(int j=;j<=tot;j++)
if(dp[i][j])
{
if(en[j]){ni=i;nj=j;break;}
if(s[i]=='.')
{
for(int w=;w<;w++)
if(!dp[i+][c[j][w]])
{ pr[i+][c[j][w]]=j;
pw[i+][c[j][w]]=w;
dp[i+][c[j][w]]=;}
}
else
{
int w=s[i]-'';
for(int j=;j<=tot;j++)
if(!dp[i+][c[j][w]])
{ pr[i+][c[j][w]]=j;
pw[i+][c[j][w]]=w;
dp[i+][c[j][w]]=;}
}
}
for(int i=ni;i>;i--)
s[i-]=pw[i][nj]='', nj=pr[i][nj];
for(int i=;i<=n;i++)
if(s[i]=='.')putchar('');
else putchar(s[i]);
puts("");
}
}
int main()
{
scanf("%d%d",&n,&m); scanf("%s",s+);
int ct=; for(int i=;i<=n;i++) ct+=(s[i]=='.');
bool fg=;
for(int i=;i<=m;i++)
{
scanf("%s",ch+); scanf("%d",&v[i]);
l[i]=strlen(ch+); dy[i]=ins(l[i]);
if(i>&&v[i]!=v[i-])fg=;
}
get_fl();
if(n<=||ct<=)solve1(); else if(!fg)S2::solve();
return ;
}

\( log(a^x) = x*log(a) \) , \( log(a*b) = log(a)+log(b) \) 。和乘法之类有关的应该考虑一下 log 。

求平均值就用 0/1 分数规划。

不要每次合法的时候都把方案存一遍。二分完再做一次,得到方案。自己用 pr[ ][ ] 和 pw[ ][ ] 记录 dp[ i ][ j ] 是从 dp[ i-1 ] 的哪里转移来的、填了什么值。

二分的 r 的范围是 max( log(v) ) 。

m 个串里没出现过的字符都是等价的。记录一下 “出现过的字符” , 如果有没出现过的就选一个加入字符集,然后用那个字符集来转移即可。

这样就能支持 1e-8 的精度了。不过 1e-4 就够了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define db double
using namespace std;
db Mx(db a,db b){return a>b?a:b;}
const int N=,K=; const db INF=3e7,eps=1e-;
int n,m,tot=,c[N][K],fl[N],q[N];
int ct[N],pr[N][N],pw[N][N]; db v[N],dp[N][N];
char s[N],ch[N],prn[N];
int tw[K],top; bool vis[K];
db get_lg(int d)
{
db l=,r=,ret=;//ret=0
while(r-l>eps)
{
db mid=(l+r)/;
if(pow(,mid)<=d)ret=mid,l=mid+eps;
else r=mid-eps;
}
return ret;
}
void ins(int len,db d)
{
int cr=;
for(int i=;i<=len;i++)
{
int w=ch[i]-'';
if(!vis[w])vis[w]=,tw[++top]=w;
if(!c[cr][w])c[cr][w]=++tot;
cr=c[cr][w];
}
v[cr]=d; ct[cr]=;
}
void get_fl()
{
int he=,tl=;
for(int i=,j;i<;i++)
if((j=c[][i]))q[++tl]=j,fl[j]=;
else c[][i]=;
while(he<tl)
{
int k=q[++he],pr=fl[k];
v[k]+=v[pr]; ct[k]+=ct[pr];
for(int i=,j;i<;i++)
if((j=c[k][i]))q[++tl]=j,fl[j]=c[pr][i];
else c[k][i]=c[pr][i];
}
}
int chk(db L)
{
for(int i=;i<=n;i++)
for(int j=;j<=tot;j++)dp[i][j]=-INF;
dp[][]=;
for(int i=;i<n;i++)
{
for(int j=;j<=tot;j++)
if(dp[i][j]>-INF)
{
if(s[i+]!='.')
{
int w=s[i+]-'',tj=c[j][w];
db d=dp[i][j]+v[tj]-L*ct[tj];
if(d>dp[i+][tj])
{
dp[i+][tj]=d;pr[i+][tj]=j;pw[i+][tj]=w;
}
continue;
}
for(int k=;k<=top;k++)
{
int w=tw[k],tj=c[j][w];
db d=dp[i][j]+v[tj]-L*ct[tj];
if(d>dp[i+][tj])
{
dp[i+][tj]=d;pr[i+][tj]=j;
pw[i+][tj]=w;
}
}
}
}
int j=;
for(int i=;i<=tot;i++)if(dp[n][i]>dp[n][j])j=i;
return j;
}
int main()
{
scanf("%d%d",&n,&m);scanf("%s",s+);
db d,l=,r=,ans;
for(int i=;i<=m;i++)
{
scanf("%s%lf",ch+,&d); d=get_lg(d);
r=Mx(r,d);//Mx not sm
int len=strlen(ch+); ins(len,d);
}
get_fl();
for(int i=;i<;i++)if(!vis[i]){tw[++top]=i;break;}
while(r-l>eps)
{
db mid=(l+r)/;
if(dp[n][chk(mid)]>)
//>0 not >=0 for almost always can =0
{ ans=mid; l=mid+eps;}
else r=mid-eps;
}
int j=chk(ans);
for(int i=n;i;i--)
prn[i]=pw[i][j]+'', j=pr[i][j];
printf("%s\n",prn+);
return ;
}

LOJ 3089 「BJOI2019」奥术神杖——AC自动机DP+0/1分数规划的更多相关文章

  1. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

  2. 【LOJ】#3089. 「BJOI2019」奥术神杖

    LOJ#3089. 「BJOI2019」奥术神杖 看见乘积就取log,开根号就是除法,很容易发现这就是一道01分数规划.. 然后建出AC自动机直接dp就行,判断条件要设成>0,因为起点的值是1, ...

  3. LOJ 3089: 洛谷 P5319: 「BJOI2019」奥术神杖

    题目传送门:LOJ #3089. 题意简述: 有一个长度为 \(n\) 的母串,其中某些位置已固定,另一些位置可以任意填. 同时给定 \(m\) 个小串,第 \(i\) 个为 \(S_i\),所有位置 ...

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

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

  5. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

  6. LOJ 3094 「BJOI2019」删数——角标偏移的线段树

    题目:https://loj.ac/problem/3094 弱化版是 AGC017C . 用线段树维护那个题里的序列即可. 对应关系大概是: 真实值的范围是 [ 1-m , n+m ] :考虑设偏移 ...

  7. LOJ 3090 「BJOI2019」勘破神机——斯特林数+递推式求通项+扩域

    题目:https://loj.ac/problem/3090 题解:https://www.luogu.org/blog/rqy/solution-p5320 1.用斯特林数把下降幂化为普通的幂次求和 ...

  8. LOJ 3093 「BJOI2019」光线——数学+思路

    题目:https://loj.ac/problem/3093 考虑经过种种反射,最终射下去的光线总和.往下的光线就是这个总和 * a[ i ] . 比如只有两层的话,设射到第二层的光线是 lst ,那 ...

  9. LOJ 3092 「BJOI2019」排兵布阵 ——DP

    题目:https://loj.ac/problem/3092 同一个人的不同城堡之间没有什么联系,只是和<=m.所以对每个城堡的 s 个值排序,做一个 f[ i ][ j ] 表示第 i 个城堡 ...

随机推荐

  1. Struts框架原理及应用

    Struts 2框架本身大致可以分为3个部分:核心控制器FilterDispatcher.业务控制器Action和用户实现的企业业务逻辑组件. 核心控制器FilterDispatcher是Struts ...

  2. ASP.NET MVC中,动态处理页面静态化

    首先解释一下什么是动态处理页面静态化 对于需要静态化的页面,第一次访问某个Action时,会先执行Action,并在页面渲染后向Response和服务器中网站的目录下都写入需要返回的html,而第二次 ...

  3. sql嵌套查询

    嵌套查询的意思是,一个查询语句(select-from-where)查询语句块可以嵌套在另外一个查询块的where子句中,称为嵌套查询.其中外层查询也称为父查询,主查询.内层查询也称子查询,从查询. ...

  4. linux bash 命令

    export:显示所有的环境变量,如果你想获取某个变量的详细信息,使用 echo $VARIABLE_NAMEv whereis:使用系统自动构建的数据库来搜索可执行文件,源文件和手册页面 which ...

  5. springboot配置文件(.yml)中自定义属性值并在controller里面获取

    1,由于项目需要,学习了新的框架--springboot,顺便练习一下在.yml中配置自定义属性并在controller里面获取.(以下的Springboot框架我已经搭建好,就不在陈述) 2,spr ...

  6. MAVEN项目不扫描mybatis的mapper.xml问题

    在使用maven+mybatis+spring在开发的时候,遇到问题,总是找不到mapper.xml文件里定义的方法.检查后发现maven编译后并没有将xml文件打包到输出路径,导致bean创建失败. ...

  7. 一道笔试题来理顺Java中的值传递和引用传递

      题目如下: private static void change(StringBuffer str11, StringBuffer str12) { str12 = str11; str11 = ...

  8. Linux安装配置rabbitmq

    Step1:安装erlang 1)下载erlang wget http://www.rabbitmq.com/releases/erlang/erlang-19.0.4-1.el7.centos.x8 ...

  9. mysql技巧:按条件筛选,然后替换

    1.按条件筛选,然后替换 select * from phome_ecms_tv where playurl like '%关键词%';update phome_ecms_tv set myorder ...

  10. 20175120彭宇辰 《Java程序设计》第六周学习总结

    教材学习内容总结 第七章 一.内部类与外部类的关系 1.内部类可以使用外嵌类的成员变量和方法.2.类体中不可以声明类变量和类方法,外部类可以用内部类声明对象.3.内部类仅供外嵌类使用.4.类声明可以使 ...