[Luogu5319][BJOI2019]奥术神杖(分数规划+AC自动机)

对最终答案取对数,得到$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 }
原文地址:https://www.cnblogs.com/HocRiser/p/10799312.html