「BJOI2019」奥术神杖
「BJOI2019」奥术神杖
Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的神器,试图借助神器的神秘力量帮助她们战胜地灾军团。
在付出了惨痛的代价后,精灵们从步步凶险的远古战场取回了一件保存尚完好的神杖。但在经历过那场所有史书都视为禁忌的“诸神黄昏之战”后,神杖上镶嵌的奥术宝石已经残缺,神力也几乎消耗殆尽。精灵高层在至高会议中决定以举国之力收集残存至今的奥术宝石,并重金悬赏天下能工巧匠修复这件神杖。
你作为神术一脉第五百零一位传人,接受了这个艰巨而神圣的使命。 神杖上从左到右镶嵌了 $n$ 颗奥术宝石,奥术宝石一共有 $10$ 种,用数字 `0123456789` 表示。有些位置的宝石已经残缺,用 `.` 表示,你需要用完好的奥术宝石填补每一处残缺的部分(每种奥术宝石个数不限,且不能够更换未残缺的宝石)。古老的魔法书上记载了 $m$ 种咒语 $(S_i,V_i)$,其中 $S_i$ 是一个非空数字串,$V_i$ 是这种组合能够激发的神力。
神杖的初始神力值 $\mathrm{Magic} = 1$,每当神杖中出现了连续一段宝石与 $S_i$ 相等时,神力值 $\mathrm{Magic}$ 就会乘以 $V_i$。但神杖如果包含了太多咒语就不再纯净导致神力降低:设 $c$ 为神杖包含的咒语个数(若咒语类别相同但出现位置不同视为多次),神杖最终的神力值为 $\sqrt[c]{\mathrm{Magic}}$。(若 $c = 0$ 则神杖最终神力值为 $1$。)
例如有两种咒语 $(01,3)$ 、$(10,4)$,那么神杖 `0101` 的神力值为 $\sqrt[3]{ 3 \times 4 \times 3}$。
你需要使修复好的神杖的最终的神力值最大,输出任何一个解即可。
Sol
考虑对贡献取对数,则乘变成加,根号变成/n。
那么问题转化为0/1分数规划。
建AC自动机,把每一个点的贡献减去ans,若存在一种填字方案使得总代价>0,那么合法。
这部分可以dp实现.令f[i][j]表示当前走到ac自动机上节点i,已经匹配前j位的最大收益。
效率O(n^2logn)
ac自动机建fail时需要转移,把fail[i]的信息加到i上.
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define db double #define maxn 3005 #define eps 1e-3 #define inf 1e9 using namespace std; int n,m,tr[maxn][10],fail[maxn],num[maxn],tot,g[maxn][maxn],b[maxn][maxn]; double v[maxn],c[maxn],f[maxn][maxn]; char T[maxn],s[maxn]; void ins(int val){ int k=0,len=strlen(s+1); for(int i=1;i<=len;i++){ if(!tr[k][s[i]-'0'])tr[k][s[i]-'0']=++tot; k=tr[k][s[i]-'0']; } num[k]++;v[k]+=log(val); } void build(){ queue<int>q; for(int i=0;i<10;i++)if(tr[0][i])q.push(tr[0][i]); while(!q.empty()){ int k=q.front();q.pop(); num[k]+=num[fail[k]]; v[k]+=v[fail[k]]; for(int i=0;i<10;i++){ if(tr[k][i])fail[tr[k][i]]=tr[fail[k]][i],q.push(tr[k][i]); else tr[k][i]=tr[fail[k]][i]; } } } db work(int k,int p){ if(g[k][p])return f[k][p]; if(p==n+1){g[k][p]=1;f[k][p]=c[k];return f[k][p];} if(T[p]=='.'){ f[k][p]=-inf; for(int i=0;i<10;i++){ db x=work(tr[k][i],p+1); if(x>f[k][p])f[k][p]=x,b[k][p]=i; } } else f[k][p]=work(tr[k][T[p]-'0'],p+1); g[k][p]=1;f[k][p]+=c[k]; return f[k][p]; } bool pd(db val){ for(int i=1;i<=tot;i++){ c[i]=v[i]-num[i]*val; } db tmp=work(0,1); for(int i=0;i<=tot;i++) for(int j=1;j<=n+1;j++)g[i][j]=0; return tmp>0; } void outp(){ int k=0; for(int i=1;i<=n;i++){ if(T[i]=='.')printf("%d",b[k][i]),k=tr[k][b[k][i]]; else printf("%c",T[i]),k=tr[k][T[i]-'0']; } puts(""); } int main(){ cin>>n>>m; scanf(" %s",T+1); for(int i=1,val;i<=m;i++){ scanf(" %s",s+1);scanf("%d",&val); ins(val); } build(); db l=0,r=20; while(l+eps<r){ db mid=(l+r)/2; if(pd(mid))l=mid; else r=mid; } pd(l);outp(); return 0; }