「BJOI2019」奥术神杖
「BJOI2019」奥术神杖
Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的神器,试图借助神器的神秘力量帮助她们战胜地灾军团。
在付出了惨痛的代价后,精灵们从步步凶险的远古战场取回了一件保存尚完好的神杖。但在经历过那场所有史书都视为禁忌的“诸神黄昏之战”后,神杖上镶嵌的奥术宝石已经残缺,神力也几乎消耗殆尽。精灵高层在至高会议中决定以举国之力收集残存至今的奥术宝石,并重金悬赏天下能工巧匠修复这件神杖。
你作为神术一脉第五百零一位传人,接受了这个艰巨而神圣的使命。 神杖上从左到右镶嵌了 颗奥术宝石,奥术宝石一共有 种,用数字 `0123456789` 表示。有些位置的宝石已经残缺,用 `.` 表示,你需要用完好的奥术宝石填补每一处残缺的部分(每种奥术宝石个数不限,且不能够更换未残缺的宝石)。古老的魔法书上记载了 种咒语 ,其中 是一个非空数字串, 是这种组合能够激发的神力。
神杖的初始神力值 ,每当神杖中出现了连续一段宝石与 相等时,神力值 就会乘以 。但神杖如果包含了太多咒语就不再纯净导致神力降低:设 为神杖包含的咒语个数(若咒语类别相同但出现位置不同视为多次),神杖最终的神力值为 。(若 则神杖最终神力值为 。)
例如有两种咒语 、,那么神杖 `0101` 的神力值为 。
你需要使修复好的神杖的最终的神力值最大,输出任何一个解即可。
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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构