1. 程式人生 > >[BJOI2019]奧術神杖(分數規劃,動態規劃,AC自動機)

[BJOI2019]奧術神杖(分數規劃,動態規劃,AC自動機)

tchar != ring -- esp fine true display sca

[BJOI2019]奧術神杖(分數規劃,動態規劃,AC自動機)

題面

洛谷

題解

首先乘法取\(log\)變加法,開\(c\)次根變成除\(c\)
於是問題等價於最大化\(\displaystyle \frac{\sum val_i}{c}\)。典型的分數規劃的形式。
二分權值\(k\),每個點的點權變成\(val_i-k\),轉為求最值,那麽直接在\(AC\)自動機上\(dp\)就行了。
註意精度問題。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define MAX 1505
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
struct Node{int son[10],ff,s;double w;}t[MAX];
int tot;
void Insert(char *s,int val)
{
    int l=strlen(s+1),u=0;
    for(int i=1;i<=l;++i)
    {
        int c=s[i]-48;
        if(!t[u].son[c])t[u].son[c]=++tot;
        u=t[u].son[c];
    }
    t[u].w=log(val);t[u].s+=1;
}
int Q[MAX],L,R;
void BuildFail()
{
    L=1;
    for(int i=0;i<10;++i)if(t[0].son[i])Q[++R]=t[0].son[i];
    while(L<=R)
    {
        int u=Q[L++];t[u].w+=t[t[u].ff].w;t[u].s+=t[t[u].ff].s;
        for(int i=0;i<10;++i)
            if(t[u].son[i])t[t[u].son[i]].ff=t[t[u].ff].son[i],Q[++R]=t[u].son[i];
            else t[u].son[i]=t[t[u].ff].son[i];
    }
}
char T[MAX],S[MAX];int n,m;
double f[MAX][MAX];int g1[MAX][MAX],g2[MAX][MAX];
void Tr(int i,int j,int k)
{
    int v=t[j].son[k];
    if(f[i][v]<f[i-1][j]+t[v].w)
    {
        f[i][v]=f[i-1][j]+t[v].w;
        g1[i][v]=j;g2[i][v]=k;
    }
}
bool check(double K)
{
    for(int i=0;i<=tot;++i)t[i].w-=K*t[i].s;
    int len=strlen(T+1);
    for(int i=0;i<=len;++i)
        for(int j=0;j<=tot;++j)f[i][j]=-1e300;
    f[0][0]=0;
    for(int i=1;i<=len;++i)
        for(int j=0;j<=tot;++j)
                if(T[i]=='.')for(int k=0;k<10;++k)Tr(i,j,k);
                else Tr(i,j,T[i]-48);
    double ans=-1e300;
    for(int i=1;i<=tot;++i)ans=max(ans,f[len][i]);
    for(int i=0;i<=tot;++i)t[i].w+=K*t[i].s;
    return ans>0;
}
int main()
{
    n=read();m=read();
    scanf("%s",T+1);
    for(int i=1,v;i<=m;++i)scanf("%s",S+1),v=read(),Insert(S,v);
    BuildFail();
    double l=0,r=21;
    while(r-l>1e-3)
    {
        double mid=(l+r)/2;
        if(check(mid))l=mid;
        else r=mid;
    }
    check(l);int pos=0,len=strlen(T+1);
    for(int i=1;i<=tot;++i)if(f[len][i]>f[len][pos])pos=i;
    for(int i=len;i;--i)S[i]=g2[i][pos]+48,pos=g1[i][pos];
    for(int i=1;i<=len;++i)putchar(S[i]);puts("");
    return 0;
}

[BJOI2019]奧術神杖(分數規劃,動態規劃,AC自動機)