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 <bits/stdc++.h>
#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];
queue<int>q;
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;i<n;++i)
{
for(int j=0;j<=tot;++j)
{
if(str[i+1]=='.')
{
for(c=0;c<10;++c)
{
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;
}
}
}
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;
}

浙公网安备 33010602011771号