标签:font 大于 一个 ret span mes can printf 记录
答案是$ans=\sqrt[c]{\prod_{i=1}^{c}v_{i}}=(\prod_{i=1}^{c}v_{i})^{\frac{1}{c}}$。
这样不大好求,我们将这个式子取$ln$,变成$ln\ ans=\frac{1}{c}\sum_{i=1}^{c}ln\ v_{i}$。
这显然是一个分数规划,每次二分一个答案$mid$,将每个串的权值都减去$mid$,那么只需要求最大价值是否大于$0$即可。
剩下的问题就是一个在$AC$自动机上的$DP$了,设$f[i][j]$表示在$AC$自动机上的点$j$,已经匹配的长度为$i$时的最大值,在$AC$自动机上转移即可。
在$DP$时还要记录一下每个状态从哪个点转移过来以便输出方案。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const double eps=1e-5; int end[2000]; double f[2000][2000]; int g[2000][2000]; int fail[2000]; int tr[2000][10]; int cnt; double val[2000]; char ch[2000]; char s[2000]; int now; int n,m; int v; double ans; void build(char *ch,int v) { int len=strlen(ch); now=0; for(int i=0;i<len;i++) { int x=ch[i]-‘0‘; if(!tr[now][x]) { tr[now][x]=++cnt; } now=tr[now][x]; } end[now]++; val[now]+=log(v); } void get_fail() { queue<int>q; for(int i=0;i<=9;i++) { if(tr[0][i]) { q.push(tr[0][i]); } } while(!q.empty()) { now=q.front(); q.pop(); val[now]+=val[fail[now]]; end[now]+=end[fail[now]]; for(int i=0;i<=9;i++) { if(tr[now][i]) { fail[tr[now][i]]=tr[fail[now]][i]; q.push(tr[now][i]); } else { tr[now][i]=tr[fail[now]][i]; } } } } double DP(double mid) { for(int i=0;i<=n;i++) { for(int j=0;j<=cnt;j++) { f[i][j]=-1e9; g[i][j]=0; } } f[0][0]=0; for(int i=0;i<n;i++) { for(int j=0;j<=cnt;j++) { if(f[i][j]>-1e9) { if(s[i+1]==‘.‘) { for(int k=0;k<=9;k++) { if(f[i+1][tr[j][k]]<f[i][j]+val[tr[j][k]]-mid*end[tr[j][k]]) { f[i+1][tr[j][k]]=f[i][j]+val[tr[j][k]]-mid*end[tr[j][k]]; g[i+1][tr[j][k]]=j; } } } else { int x=s[i+1]-‘0‘; if(f[i+1][tr[j][x]]<f[i][j]+val[tr[j][x]]-mid*end[tr[j][x]]) { f[i+1][tr[j][x]]=f[i][j]+val[tr[j][x]]-mid*end[tr[j][x]]; g[i+1][tr[j][x]]=j; } } } } } double res=-1e9; for(int i=0;i<=cnt;i++) { res=max(res,f[n][i]); } return res; } void print(int dep,int now) { if(!dep) { return ; } print(dep-1,g[dep][now]); if(s[dep]!=‘.‘) { printf("%c",s[dep]); return ; } else { for(int i=0;i<=9;i++) { if(tr[g[dep][now]][i]==now) { printf("%c",i+‘0‘); return ; } } } } int main() { scanf("%d%d",&n,&m); scanf("%s",s+1); for(int i=1;i<=m;i++) { scanf("%s%d",ch,&v); build(ch,v); } get_fail(); double l=0,r=22; while(r-l>eps) { double mid=(l+r)/2; if(DP(mid)>0) { l=mid; ans=mid; } else { r=mid; } } DP(ans); for(int i=0;i<=cnt;i++) { if(f[n][i]>0) { print(n,i); break; } } }
[BJOI2019]奥术神杖——AC自动机+DP+分数规划+二分答案
标签:font 大于 一个 ret span mes can printf 记录
原文地址:https://www.cnblogs.com/Khada-Jhin/p/10772191.html