luoguP5319 [BJOI2019]奥术神杖 AC自动机+01分数规划+dp

显然取对数,然后二分答案进行 01 分数规划.

设 $f[i][j]$ 表示在 AC 自动机上的点 $i$ ,匹配到了 $j$ 位的最大价值.

转移的时候判断一下当前是点还是数字,然后在 AC 自动机上的终止节点上算一下贡献就行.   

构建 AC 自动机的时候要注意:点 $i$ 的价值是 val[i]+i祖先的贡献和.   

然后 01 分数规划的时候要注意精度,且要判断 f[n][i] 是否大于 0,不能直接判 f[j][i].   

code:  

#include    
#define N 1608   
#define inf 100000   
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
const double eps=1e-7;     
struct data 
{
    int ch[10],f,fl,sum;       
    double det;   
}s[N];     
queueq;    
double f[N][N];  
char str[N],ar[N];       
int tot,n,m,loc[N],pre[N][N],col[N][N];                 
void ins(int d) 
{
    int x=0,y,z,c; 
    scanf("%s%d",ar+1,&z),y=strlen(ar+1);   
    for(int i=1;i<=y;++i) 
    {
        c=ar[i]-'0';    
        if(!s[x].ch[c]) s[x].ch[c]=++tot;  
        x=s[x].ch[c];   
    }   
    s[x].fl=1,s[x].det+=log(z),loc[d]=x,s[x].sum++;       
}
void build() 
{ 
    for(int i=0;i<10;++i)  if(s[0].ch[i]) q.push(s[0].ch[i]);   
    while(!q.empty()) 
    {
        int u=q.front(); q.pop();   
        for(int i=0;i<10;++i) 
        {
            if(!s[u].ch[i]) { s[u].ch[i]=s[s[u].f].ch[i]; continue; }   
            int v=s[u].ch[i]; 
            s[v].f=s[s[u].f].ch[i];    
            s[v].sum+=s[s[v].f].sum;  
            s[v].det+=s[s[v].f].det;   
            q.push(v);   
        }
    }
}
int check(double t) 
{   
    int x,y,z=0,c;
    for(int i=1;i<=tot;++i)  s[i].det-=s[i].sum*t;              
    for(int i=0;i<=n;++i) for(int j=0;j<=tot;++j) f[i][j]=-inf;   
    f[0][0]=0;               
    for(int i=0;if[i+1][s[j].ch[c]]) 
                    { 
                        f[i+1][s[j].ch[c]]=f[i][j]+s[s[j].ch[c]].det;         
                        pre[i+1][s[j].ch[c]]=j;   
                        col[i+1][s[j].ch[c]]=c;    
                    }
                }
            } 
            else 
            { 
                c=str[i+1]-'0';       
                if(f[i][j]+s[s[j].ch[c]].det>f[i+1][s[j].ch[c]]) 
                { 
                    f[i+1][s[j].ch[c]]=f[i][j]+s[s[j].ch[c]].det;       
                    pre[i+1][s[j].ch[c]]=j;   
                    col[i+1][s[j].ch[c]]=c;    
                }
            }
        }
    }
    for(int i=0;i<=tot;++i)  if(f[n][i]>0) z=1;  
    for(int i=1;i<=tot;++i)  s[i].det+=s[i].sum*t;              
    return z;    
}   
void print(int x,int l) 
{
    if(l>1) print(pre[l][x],l-1);   
    printf("%d",col[l][x]);   
}
int main() 
{ 
    // setIO("input");   
    scanf("%d%d%s",&n,&m,str+1);      
    for(int i=1;i<=m;++i) ins(i);  
    build();  
    // 注意这里 01 分数规划的写法  !!   
    double l=0.0,r=log(1e9+9),mid,ans;   
    while(r-l>=eps) 
    {   
        mid=(l+r)*0.5;        
        if(check(mid)) l=mid;    
        else r=mid;   
    }                    
    check(l);      
    for(int i=0;i<=tot;++i)    if(f[n][i]>0) { print(i,n); break;  }   
    return 0;   
}

  

你可能感兴趣的:(luoguP5319 [BJOI2019]奥术神杖 AC自动机+01分数规划+dp)