对最终答案取对数,得到$ln(Ans)=frac{1}{c}sum ln(v_i)$,典型的分数规划问题。
二分答案后,对所有咒语串建立AC自动机,然后套路地$f[i][j]$表示走到T的第i个字符,当前在自动机的第j个位置,能得到的最大收益。
注意二分的r初始不能设太大,25就可以了,二分终止的eps最好设到1e-5,否则会WA或者TLE。
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 using namespace std; 7 8 const int N=2010,inf=1e9; 9 const double eps=1e-5; 10 char S[N],ss[N][N],pre[N][N]; 11 int n,m,x,nd,sz[N],fail[N],q[N],son[N][11],fr[N][N]; 12 double v[N],w[N],f[N][N]; 13 14 void add(char s[],double v){ 15 int x=0,len=strlen(s+1); 16 rep(i,1,len){ 17 int c=s[i]-'0'; 18 if (!son[x][c]) son[x][c]=++nd; 19 x=son[x][c]; 20 } 21 w[x]+=v; sz[x]++; 22 } 23 24 void bfs(){ 25 int st=0,ed=0; 26 rep(i,0,9) if (son[0][i]) q[++ed]=son[0][i]; 27 while (st!=ed){ 28 int x=q[++st]; 29 rep(i,0,9) if (son[x][i]) fail[son[x][i]]=son[fail[x]][i],q[++ed]=son[x][i]; 30 else son[x][i]=son[fail[x]][i]; 31 w[x]+=w[fail[x]]; sz[x]+=sz[fail[x]]; 32 } 33 } 34 35 double solve(double mid){ 36 rep(i,0,n) rep(j,0,nd) f[i][j]=-inf; 37 f[0][0]=0; double ans=-inf; 38 rep(i,0,n-1) rep(j,0,nd){ 39 if (S[i+1]!='.'){ 40 int t=son[j][S[i+1]-'0']; 41 if (f[i][j]+w[t]-mid*sz[t]>f[i+1][t]) 42 f[i+1][t]=f[i][j]+w[t]-mid*sz[t],fr[i+1][t]=j; 43 continue; 44 } 45 rep(c,0,9){ 46 int t=son[j][c]; 47 if (f[i][j]+w[t]-mid*sz[t]>f[i+1][t]) 48 f[i+1][t]=f[i][j]+w[t]-mid*sz[t],fr[i+1][t]=j,pre[i+1][t]=c+'0'; 49 } 50 } 51 rep(i,0,nd) ans=max(ans,f[n][i]); return ans; 52 } 53 54 void Print(int i,int j){ 55 if (!i) return; 56 Print(i-1,fr[i][j]); 57 if (S[i]!='.') putchar(S[i]); else putchar(pre[i][j]); 58 } 59 60 int main(){ 61 freopen("arcana.in","r",stdin); 62 freopen("arcana.out","w",stdout); 63 scanf("%d%d%s",&n,&m,S+1); 64 rep(i,1,m) scanf("%s%d",ss[i]+1,&x),v[i]=log(x),add(ss[i],v[i]); 65 double L=0,R=25; bfs(); 66 while (L+eps<R){ 67 double mid=(L+R)/2; 68 if (solve(mid)>0) L=mid; else R=mid; 69 } 70 solve(L); int mn=-inf,mnd=0; 71 rep(i,0,nd) if (f[n][i]>mn) mn=f[n][i],mnd=i; 72 Print(n,mnd); 73 return 0; 74 }