[BJOI2019]奥术神杖
https://www.luogu.com.cn/problem/P5319
把题目要求最大化的那个式子,取一手对数,然后就变成了一个分数规划问题。
二分后AC自动机上DP即可。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define N 2200
#define inf (1e9+7)
#define eps (1e-7)
#define db double
#define ll long long
#define ldb long double
using namespace std;
inline int read()
{
char ch=0;
int x=0,flag=1;
while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch-'0');ch=getchar();}
return x*flag;
}
queue<int>q;
char s[N],ch[N];
int root=1,size=1,f[N],nxt[N][10];
int insert()
{
scanf("%s",s+1);
int n=strlen(s+1),x=root;
for(int i=1;i<=n;i++)
{
int k=s[i]-'0';
if(!nxt[x][k])nxt[x][k]=++size;
x=nxt[x][k];
}
return x;
}
void build()
{
q.push(root);
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=0;i<10;i++)
if(nxt[x][i])
{
if(x!=root)f[nxt[x][i]]=nxt[f[x]][i];
else f[nxt[x][i]]=root;
q.push(nxt[x][i]);
}
else
{
if(x!=root)nxt[x][i]=nxt[f[x]][i];
else nxt[x][i]=root;
}
}
}
db w[N],dp[N][N];
int n,m,res,p[N],sz[N],pre[N][N];
struct edge{int to,nxt;}e[N*2];
int num,head[N];
inline void add(int x,int y)
{
e[++num]={y,head[x]};head[x]=num;
e[++num]={x,head[y]};head[y]=num;
}
void dfs(int x,int fa)
{
w[x]+=w[fa];sz[x]+=sz[fa];
for(int i=head[x];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(to==fa)continue;
dfs(to,x);
}
}
bool check(db mid)
{
for(int i=0;i<=n;i++)
for(int x=0;x<=size;x++)
dp[i][x]=-inf,pre[i][x]=0;
dp[0][root]=0;
for(int i=0;i<n;i++)
for(int x=1;x<=size;x++)
if(dp[i][x]+inf>1)
{
if(ch[i+1]=='.')
{
for(int k=0;k<10;k++)
{
int to=nxt[x][k];
if(dp[i+1][to]<dp[i][x]+(w[to]-sz[to]*mid))
{
pre[i+1][to]=x;
dp[i+1][to]=dp[i][x]+(w[to]-sz[to]*mid);
}
}
}
else
{
int to=nxt[x][ch[i+1]-'0'];
if(dp[i+1][to]<dp[i][x]+(w[to]-sz[to]*mid))
{
pre[i+1][to]=x;
dp[i+1][to]=dp[i][x]+(w[to]-sz[to]*mid);
}
}
}
db ans=-inf;
for(int x=1;x<=size;x++)if(dp[n][x]>ans)res=x,ans=dp[n][x];
return (ans>0);
}
int main()
{
n=read();m=read();scanf("%s",ch+1);
for(int i=1;i<=m;i++)
{
int x=insert();
sz[x]=1;w[x]=log(read());
}
build();
num=-1;memset(head,-1,sizeof(head));
for(int i=2;i<=size;i++)add(f[i],i);
dfs(root,root);
db l=0,r=30,mid;
for(int i=1;i<=15;i++)
{
mid=(l+r)/2.0;
if(check(mid))l=mid;else r=mid;
}
check(l);
for(int i=n,x=res;i>=0;i--)p[i]=x,x=pre[i][x];
for(int i=0;i<n;i++)
{
int x=p[i];
if(ch[i+1]=='.')
{
for(int k=0;k<10;k++)
if(nxt[x][k]==p[i+1]){printf("%d",k);break;}
}
else printf("%c",ch[i+1]);
}
return 0;
}