[BJOI2019]奥术神杖
Problem
Solution
根据题意,需要知道神杖所包含了哪些咒语,大可使用自动ac自动机。
关键是如何处理。 这其实也很套路,一眼看过去,觉得根号这个东西显然就不可做,于是想办法消去根号。于是对于这个式子取对数,
对于这种形式的答案,很显然可以01分数规划来解决(其实就是二分答案),然后在ac自动机上去dp,check当前二分的值是否可行即可。
Code
代码常数较大(luogu上O2可过),大可适当调整
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 3000 + 5;
const int MAX_M = 3000 + 5;
const int MaxChar = 10;
const double inf = 0x3f3f3f3f;
const double eps = 1e-4;
int n,m;
char a[MAX_M];
struct AhoCorasickAutoMaton{
int sz[MAX_M],root,tot,nxt[MAX_M][MaxChar],fail[MAX_M];
double val[MAX_M],num[MAX_M];
AhoCorasickAutoMaton(){tot=root=1;}
inline void insert(char *s,int Val){
int p=1;
int len=strlen(s+1);
for(int i=1;i<=len;++i){
int c=s[i]-'0';
if(!nxt[p][c]) nxt[p][c]=++tot;
p=nxt[p][c];
}
val[p]=log(Val);
num[p]++;
}
inline void build(){
queue<int> q;
fail[root]=root;
for(int i=0;i<MaxChar;++i){
int v=nxt[root][i];
fail[v]=root;
if(v) q.push(v);
else nxt[root][i]=root;
}
while(q.size()){
int u=q.front();q.pop();
for(int i=0;i<MaxChar;++i){
if(nxt[u][i]){
int v=nxt[u][i];
if(u==root) fail[v]=root;
else fail[v]=nxt[fail[u]][i];
q.push(v);
val[v]+=val[fail[v]];
num[v]+=num[fail[v]];
}
else nxt[u][i]=nxt[fail[u]][i];
}
}
}
double f[MAX_N][MAX_N],w[MAX_M];
bool vis[MAX_N][MAX_N];
int pos[MAX_N][MAX_N];
double dfs(int p,int x){
if(vis[p][x])return f[p][x];
if(x==n+1){vis[p][x]=1;f[p][x]=w[p];return f[p][x];}
if(a[x]=='.'){
f[p][x]=-inf;
for(int i=0;i<10;i++){
double tmp=dfs(nxt[p][i],x+1);
if(tmp>f[p][x]){
f[p][x]=tmp,pos[p][x]=i;
}
}
}
else f[p][x]=dfs(nxt[p][a[x]-'0'],x+1),pos[p][x]=a[x]-'0';
vis[p][x]=1;f[p][x]+=w[p];
return f[p][x];
}
inline bool check(double mid){
memset(vis,0,sizeof(vis));
for(int i=1;i<=tot;++i) w[i]=val[i]-(double)num[i]*mid;
return dfs(1,1)>0;
}
inline void print(){
int p=1;
for(int i=1;i<=n;++i){
printf("%d",pos[p][i]);
p=nxt[p][pos[p][i]];
}
}
inline void calc(){
double l=0,r=20;
while(l+eps<r){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=tot;++i) w[i]=val[i]-(double)num[i]*l;
dfs(1,1);
print();
}
}acam;
char s[MAX_M];
int main(){
scanf("%d%d",&n,&m);
scanf("%s",a+1);
for(int i=1;i<=m;++i){
scanf("%s",s+1);
int val;
scanf("%d",&val);
acam.insert(s,val);
}
acam.build();
acam.calc();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现