zoukankan      html  css  js  c++  java
  • 「BJOI2019」奥术神杖

    「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 imes 4 imes 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;
    }
  • 相关阅读:
    九度-题目1197:奇偶校验
    九度-题目1073:杨辉三角形
    九度-题目1072:有多少不同的面值组合?
    同步异步,阻塞非阻塞
    注解方式配置bean
    监听器
    自定义系统初始化器
    构建流
    数值流
    流的使用
  • 原文地址:https://www.cnblogs.com/liankewei/p/12237561.html
Copyright © 2011-2022 走看看