[BJOI2019]奥术神杖
Problem
Solution
根据题意,需要知道神杖所包含了哪些咒语,大可使用自动ac自动机。
关键是如何处理\(\sqrt[c]{Magic}\)。 这其实也很套路,一眼看过去,觉得根号这个东西显然就不可做,于是想办法消去根号。于是对于这个式子取对数,\(\sqrt[c]{Magic}=\frac{Magic}{c}[Magic = V_1 + V_2 + ... + V_n]\)
对于\(\frac{Magic}{c}\)这种形式的答案,很显然可以01分数规划来解决(其实就是二分答案),然后在ac自动机上去dp,check当前二分的值是否可行即可。
Code
代码常数较大(luogu上O2可过),大可适当调整\(eps\)
#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;
}