码迷,mamicode.com
首页 > 其他好文 > 详细

「BJOI2019」奥术神杖

时间:2020-01-28 12:11:19      阅读:63      评论:0      收藏:0      [点我收藏+]

标签:turn   lib   %s   print   ble   生活   需要   i++   转移   

「BJOI2019」奥术神杖

Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的神器,试图借助神器的神秘力量帮助她们战胜地灾军团。

在付出了惨痛的代价后,精灵们从步步凶险的远古战场取回了一件保存尚完好的神杖。但在经历过那场所有史书都视为禁忌的“诸神黄昏之战”后,神杖上镶嵌的奥术宝石已经残缺,神力也几乎消耗殆尽。精灵高层在至高会议中决定以举国之力收集残存至今的奥术宝石,并重金悬赏天下能工巧匠修复这件神杖。

你作为神术一脉第五百零一位传人,接受了这个艰巨而神圣的使命。 神杖上从左到右镶嵌了 $n$ 颗奥术宝石,奥术宝石一共有 $10$ 种,用数字 `0123456789` 表示。有些位置的宝石已经残缺,用 `.` 表示,你需要用完好的奥术宝石填补每一处残缺的部分(每种奥术宝石个数不限,且不能够更换未残缺的宝石)。古老的魔法书上记载了 $m$ 种咒语 $(S_i,V_i)$,其中 $S_i$ 是一个非空数字串,$V_i$ 是这种组合能够激发的神力。

神杖的初始神力值 $\mathrm{Magic} = 1$,每当神杖中出现了连续一段宝石与 $S_i$ 相等时,神力值 $\mathrm{Magic}$ 就会乘以 $V_i$。但神杖如果包含了太多咒语就不再纯净导致神力降低:设 $c$ 为神杖包含的咒语个数(若咒语类别相同但出现位置不同视为多次),神杖最终的神力值为 $\sqrt[c]{\mathrm{Magic}}$。(若 $c = 0$ 则神杖最终神力值为 $1$。)

例如有两种咒语 $(01,3)$ 、$(10,4)$,那么神杖 `0101` 的神力值为 $\sqrt[3]{ 3 \times 4 \times 3}$。

你需要使修复好的神杖的最终的神力值最大,输出任何一个解即可。


Sol

考虑对贡献取对数,则乘变成加,根号变成/n。

那么问题转化为0/1分数规划。

建AC自动机,把每一个点的贡献减去ans,若存在一种填字方案使得总代价>0,那么合法。

这部分可以dp实现.令f[i][j]表示当前走到ac自动机上节点i,已经匹配前j位的最大收益。

效率O(n^2logn)

ac自动机建fail时需要转移,把fail[i]的信息加到i上.

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define db double
#define maxn 3005
#define eps 1e-3
#define inf 1e9
using namespace std;
int n,m,tr[maxn][10],fail[maxn],num[maxn],tot,g[maxn][maxn],b[maxn][maxn];
double v[maxn],c[maxn],f[maxn][maxn];
char T[maxn],s[maxn];
void ins(int val){
    int k=0,len=strlen(s+1);
    for(int i=1;i<=len;i++){
        if(!tr[k][s[i]-0])tr[k][s[i]-0]=++tot;
        k=tr[k][s[i]-0];
    }
    num[k]++;v[k]+=log(val);
}
void build(){
    queue<int>q;
    for(int i=0;i<10;i++)if(tr[0][i])q.push(tr[0][i]);
    while(!q.empty()){
        int k=q.front();q.pop();
        num[k]+=num[fail[k]];
        v[k]+=v[fail[k]];
        for(int i=0;i<10;i++){
            if(tr[k][i])fail[tr[k][i]]=tr[fail[k]][i],q.push(tr[k][i]);
            else tr[k][i]=tr[fail[k]][i];
        }
    }
}
db work(int k,int p){
    if(g[k][p])return f[k][p];
    if(p==n+1){g[k][p]=1;f[k][p]=c[k];return f[k][p];}
    if(T[p]==.){
        f[k][p]=-inf;
        for(int i=0;i<10;i++){
            db x=work(tr[k][i],p+1);
            if(x>f[k][p])f[k][p]=x,b[k][p]=i;
        }
    }
    else f[k][p]=work(tr[k][T[p]-0],p+1);
    g[k][p]=1;f[k][p]+=c[k];
    return f[k][p];
}
bool pd(db val){
    for(int i=1;i<=tot;i++){
        c[i]=v[i]-num[i]*val;
    }
    db tmp=work(0,1);
    for(int i=0;i<=tot;i++)
    for(int j=1;j<=n+1;j++)g[i][j]=0;
    return tmp>0;
}
void outp(){
    int k=0;
    for(int i=1;i<=n;i++){
        if(T[i]==.)printf("%d",b[k][i]),k=tr[k][b[k][i]];
        else printf("%c",T[i]),k=tr[k][T[i]-0];
    }
    puts("");
}
int main(){
    cin>>n>>m;
    scanf(" %s",T+1);
    for(int i=1,val;i<=m;i++){
        scanf(" %s",s+1);scanf("%d",&val);
        ins(val);
    }
    build();
    db l=0,r=20;
    while(l+eps<r){
        db mid=(l+r)/2;
        if(pd(mid))l=mid;
        else r=mid;
    }
    
    pd(l);outp();
    return 0;
}

 

「BJOI2019」奥术神杖

标签:turn   lib   %s   print   ble   生活   需要   i++   转移   

原文地址:https://www.cnblogs.com/liankewei/p/12237561.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!