luogu P5319 [BJOI2019]奥术神杖
题面传送门
蛮好地题目。
刚开始没有想到取对数然后二分答案对于每个权值除\(mid\)看答案乘积是否大于\(1\)
然后就被卡常了,搞了一下循环展开才勉强不开O2卡过去。
具体思路就是对模式串建出AC自动机然后在AC自动机上设\(f_{i,j}\)为匹配到了\(i\)点然后在AC自动机上第\(j\)个节点。
这个东西滚动一下就可以卡cache了。
然后改成二分对数就跑得很快,直接最优解。
时间复杂度\(O(nmlogw)\)
code:
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<algorithm>
#include<bitset>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define l(x) x<<1
#define r(x) x<<1|1
#define re register
#define ll long long
#define db double
#define S 10000000
#define N 1500
#define eps (1e-6)
using namespace std;
int n,m,w[N+5],pl[N+5],len;char c[N+5],a[N+5];db l,r,mid;
struct yyy{int to,z;};
struct ljb{
int head,h[N+5];yyy f[N+5];
I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}
}s;
struct AC{
int son[N+5][11],fail[N+5],now,i,j,cnt,from[N+5][N+5],f[N+5];db v[N+5],dp[N+5],ans;queue<int>q;
I void insert(int id){for(now=0,i=1;i<=len;i++) !son[now][a[i]-'0']&&(son[now][a[i]-'0']=++cnt),now=son[now][a[i]-'0'];pl[id]=now;}
I void bfs(){
for(i=0;i<=9;i++) son[0][i]&&(q.push(son[0][i]),0);
while(!q.empty()){
now=q.front();q.pop();
for(i=0;i<=9;i++) !son[now][i]?(son[now][i]=son[fail[now]][i]):(q.push(son[now][i]),fail[son[now][i]]=son[fail[now]][i]);
}
}
I void dfs(int x,db w){
yyy tmp;w+=v[x];v[x]=w;for(int cur=s.h[x];cur;cur=tmp.z)tmp=s.f[cur],dfs(tmp.to,w);
}
I int check(db mid){
re int i,j,nows,last,now;re db ans,g[2][N+5];memset(v,0,sizeof(v));memset(g[0],-0x3f,sizeof(g[0]));g[0][0]=0;
for(i=1;i<=m;i++) v[pl[i]]+=log(w[i])-mid;dfs(0,0);
for(i=0;i<n;i++){
nows=i&1;last=nows^1;memset(g[last],-0x3f,sizeof(g[last]));
for(j=0;j<=cnt;j++) {
if(c[i+1]!='.'-'0'){
now=son[j][c[i+1]];
g[last][now]<g[nows][j]+v[now]&&(g[last][now]=g[nows][j]+v[now],from[i+1][now]=j);
}
else{
now=son[j][0],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][1],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][2],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][3],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][4],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][5],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][6],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][7],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][8],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
now=son[j][9],g[last][now]<(ans=g[nows][j]+v[now])&&(g[last][now]=ans,from[i+1][now]=j);
}
}
}
for(ans=-1e9,i=0;i<=cnt;i++)ans=max(ans,g[n&1][i]),dp[i]=g[n&1][i];return ans>0;
}
I void getans(db mid){
check(mid);for(now=0,i=1;i<=cnt;i++) dp[i]>dp[now]&&(now=i);
for(i=n;i;i--) f[i]=now,now=from[i][now];
for(i=1;i<=n;i++){
if(c[i]!='.'-'0'){putchar(c[i]+'0');continue;}
for(j=0;j<=9;j++) if(son[f[i-1]][j]==f[i]){putchar(j+'0');break;}
}
}
}g;
int main(){
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
re int i;scanf("%d%d%s",&n,&m,c+1);for(i=1;i<=n;i++) c[i]-='0';for(i=1;i<=m;i++) scanf("%s%d",a+1,&w[i]),len=strlen(a+1),g.insert(i);g.bfs();l=0;r=30;
for(i=1;i<=g.cnt;i++) s.add(g.fail[i],i);while(l+eps<r) mid=(l+r)/2,(g.check(mid)?l:r)=mid;/*printf("%lf\n",l);*/g.getans(l);
}