[BJOI2019]奥术神杖

题目

我要是生在\(bj\)估计就凉了,一道\(d1t1\)做了\(4h+\)

首先看到这个鬼畜的计算贡献的柿子

\[\prod_{i=1}^na_i^{\frac{b_i}{\sum_{j=1}^nb_j}} \]

\(a_i\)为第\(i\)个字符串的价值,\(b_i\)为第\(i\)个字符串出现的次数

我们显然需要取一个\(\ln\)

就变成了

\[\sum_{i=1}^n\frac{b_i}{\sum_{j=1}^nb_j}\ln a_i \]

这样写太难看了

我们随便一些就变成了

\[\frac{\sum_{i=1}^nb_i\ln a_i}{\sum_{i=1}^nb_i} \]

一看这不分数规划吗,我们直接二分一个\(mid\)看看答案是否能更大就好了

如果更大

\[\frac{\sum_{i=1}^nb_i\ln a_i}{\sum_{i=1}^nb_i}>mid \]

也就是

\[\sum_{i=1}^nb_i(\ln a_i-mid)>0 \]

显然这又是一个多串匹配的问题,我们直接上\(AC\)自动机

现在的问题就是在自动机上找到一条长度为\(n\)的路径,点权和最大

直接在自动机上按位\(dp\)就好了,\(dp[i][j]\)表示匹配的长度是\(i\)当前在自动机上的第\(j\)个节点的最大路径长度

代码

#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const double eps=1e-12;
const int maxn=1505;
struct E{int v,nxt;}e[maxn];
int n,m,cnt,num,L;
char S[maxn],T[maxn];
int id[maxn],fa[maxn],vis[2][maxn],son[maxn][10];
int r[maxn][maxn],c[maxn][maxn],head[maxn],tmp[maxn];
double val[maxn],d[maxn];
double dp[2][maxn],f[maxn][maxn];
inline void add(int x,int y) {
    e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void Dfs(int x) {
    for(re int i=head[x];i;i=e[i].nxt)
        d[e[i].v]+=d[x],Dfs(e[i].v);
}
inline int pd(double a) {return a+eps>0&&a-eps<0;}
inline void ins(int j) {
    int v;
    scanf("%s",T+1);scanf("%d",&v);
    int now=0,len=strlen(T+1);L+=len;
    for(re int i=1;i<=len;i++) {
        if(!son[now][T[i]-'0']) son[now][T[i]-'0']=++cnt;
        now=son[now][T[i]-'0'];
    }
    tmp[now]++;id[j]=now;val[j]=log(v);
}
inline void Build() {
    std::queue<int> q;
    for(re int i=0;i<=9;i++) if(son[0][i]) q.push(son[0][i]);
    while(!q.empty()) {
        int k=q.front();q.pop();
        add(fa[k],k);tmp[k]+=tmp[fa[k]];
        for(re int i=0;i<=9;i++)
        if(son[k][i]) fa[son[k][i]]=son[fa[k]][i],q.push(son[k][i]);
            else son[k][i]=son[fa[k]][i];
    }
}
inline void Pre_work(double mid) {
    for(re int i=0;i<=cnt;i++) d[i]=0;
    for(re int i=1;i<=m;i++) d[id[i]]+=val[i]-mid;
    Dfs(0);
}
inline int check(double mid) {
    Pre_work(mid);
    memset(dp,-20,sizeof(dp));
    dp[0][0]=0;int o=0;vis[0][0]=1;
    for(re int i=0;i<n;i++,o^=1)  {
        memset(dp[o^1],-20,sizeof(dp[o^1]));
        memset(vis[o^1],0,sizeof(vis[o^1]));
        for(re int j=0;j<=cnt;j++) {
            if(!vis[o][j]) continue;
            if(S[i+1]>='0'&&S[i+1]<='9') {
                int v=son[j][S[i+1]-'0'];
                vis[o^1][v]=1;
                dp[o^1][v]=max(dp[o^1][v],dp[o][j]+d[v]);
                continue;
            }
            for(re int k=0;k<=9;k++) {
                int v=son[j][k];
                vis[o^1][v]=1;
                dp[o^1][v]=max(dp[o^1][v],dp[o][j]+d[v]);
            }
        }
    }
    for(re int i=0;i<=cnt;i++) 
    if(dp[o][i]>0&&!pd(dp[o][1])) 
        return 1;
    return 0;
}
void dfs(int len,int now) {
    if(!len) return;
    dfs(len-1,c[len][now]);
    putchar(r[len][now]+'0');
}
inline void solve(double mid) {
    Pre_work(mid);
    memset(f,-20,sizeof(f));
    f[0][0]=0;
    for(re int i=0;i<n;i++) 
        for(re int j=0;j<=cnt;j++) {
            if(S[i+1]>='0'&&S[i+1]<='9') {
                int v=son[j][S[i+1]-'0'];
                if(f[i][j]+d[v]>f[i+1][v]) {
                    c[i+1][v]=j;r[i+1][v]=S[i+1]-'0';
                    f[i+1][v]=f[i][j]+d[v];
                }
                continue;
            }
            for(re int k=0;k<=9;k++) {
                int v=son[j][k];
                if(f[i][j]+d[v]>f[i+1][v]) {
                    c[i+1][v]=j,r[i+1][v]=k;
                    f[i+1][v]=f[i][j]+d[v];
                }
            }
        }
    int t=0;
    for(re int i=1;i<=cnt;i++)
    if(f[n][i]>f[n][t]) t=i;
    dfs(n,t);
}
namespace sub1 {
    char g[15];
    double ans=0;
    void dfs(int x) {
        if(x==n+1) {
            double w=0;int y=0,now=0;
            for(re int i=1;i<=n;i++) {
                now=son[now][S[i]-'0'];
                y+=tmp[now];w+=d[now];
            }
            if(w/(1.0*y)>ans) {
                ans=w/(1.0*y);
                for(re int i=1;i<=n;i++) g[i]=S[i];
            }
            return;
        } 
        if(S[x]!='.') {
            dfs(x+1);
            return;
        }
        for(re int i=0;i<10;i++) {
            S[x]=i+48;
            dfs(x+1);
        }
        S[x]='.';
        
    }
    inline void solve() {
        Pre_work(0);
        dfs(1);
        for(re int i=1;i<=n;i++) putchar(g[i]);
    }
}
int main() {
    scanf("%d%d",&n,&m);
    scanf("%s",S+1);
    for(re int i=1;i<=m;i++) ins(i);
    Build();
    if(n<=6&&L<=20) {
        sub1::solve();
        return 0;
    }
    double l=0,r=1e5+5,ans=0;
    while(r-l>eps) {
        double mid=(l+r)/2.0;
        if(check(mid)) l=mid,ans=mid;
            else r=mid;
    }
    solve(ans);
    return 0;
}



posted @ 2019-04-22 07:06  asuldb  阅读(111)  评论(0编辑  收藏  举报