( ext{Description})
( ext{Solution})
发现有 (ans=sqrt[c]{prod_{i=1}^c v_i})。不过显然这个东西直接算出来会爆。
有一个操作就是同时取对数。就有:
[ln ans=lnleft(sqrt[c]{prod_{i=1}^c v_i}
ight )
]
[ln ans=frac{1}{c} lnleft(prod_{i=1}^c v_i
ight )
]
[ln ans=frac{1}{c} sum_{i=1}^c ln v_i
]
因为 (e>1),(ln(x)) 是单增的,所以就是求 (frac{1}{c} sum_{i=1}^c ln v_i) 的最大值。观察柿子,有一个未知的求和柿子和与之相配的系数。想到了什么?(0/1) 分数规划!
所以二分答案 (mid),若有:
[mid<frac{1}{c} sum_{i=1}^c ln v_i
]
[ccdot mid<sum_{i=1}^c ln v_i
]
[sum_{i=1}^c left(ln v_i-mid
ight)>0
]
我们可以对于每个 (mid),求出 (sum_{i=1}^c left(ln v_i-mid ight)) 的最大值(这里的所有 (v) 显然是自己选定)。如果满足最大值 (>0),说明 (mid) 还可以取到更大(带回原来的柿子看)。
对于求出 (sum_{i=1}^c left(ln v_i-mid ight)) 的最大值,显然就是将单个咒语权值减 (mid),用 ( ext{AC}) 自动姬统计某个后缀出现的权值(这个后缀可能包含很多咒语),然后大力 ( ext{DP})。
设 (f[i][j]) 为到神杖的第 (i) 位,在 ( ext{AC}) 自动姬上的节点 (j) 的最大值。(如果不是很懂为什么要取节点 (0),可以看看我的这篇博客)
就有:
[f_{i+1,son_{j,k}}=max{f_{i,j}+val_{son_{j,k}},f_{i+1,son_{j,k}}}
]
(son_{j,k}) 就是本轮添加字符 (k) 对应的节点。
( ext{Code})
#include <cstdio>
#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}
#include <cmath>
#include <queue>
#include <cstring>
using namespace std;
const int maxn=1505;
const double eps=1e-6,inf=1e11;
int n,m,Value,t[maxn][12],tot,num[maxn],f[maxn],memo[maxn][maxn][2];
char s[maxn],ori[maxn],ans[maxn];
double val[maxn],dp[maxn][maxn];
queue <int> q;
void Insert() {
int p=0,len=strlen(s+1);
rep(i,1,len) {
int d=s[i]-'0';
if(!t[p][d]) t[p][d]=++tot;
p=t[p][d];
}
++num[p],val[p]+=log2(Value);
}
void GetFail() {
rep(i,0,9) if(t[0][i]) q.push(t[0][i]);
while(!q.empty()) {
int u=q.front(); q.pop();
val[u]+=val[f[u]],num[u]+=num[f[u]];
rep(i,0,9)
if(t[u][i]) f[t[u][i]]=t[f[u]][i],q.push(t[u][i]);
else t[u][i]=t[f[u]][i];
}
}
bool ok(double x) {
rep(i,0,tot) val[i]-=x*num[i];
rep(i,0,n) rep(j,0,tot) dp[i][j]=-inf;
dp[0][0]=0;
rep(i,0,n-1) rep(j,0,tot) rep(k,0,9)
if(ori[i+1]=='.'||ori[i+1]==k+'0') {
if(dp[i+1][t[j][k]]<dp[i][j]+val[t[j][k]]) {
dp[i+1][t[j][k]]=dp[i][j]+val[t[j][k]];
memo[i+1][t[j][k]][0]=j,memo[i+1][t[j][k]][1]=k;
}
}
rep(i,0,tot) val[i]+=x*num[i];
int pos=0;
rep(i,1,tot) if(dp[n][i]>dp[n][pos]) pos=i; // 找匹配 n 位后值最大
if(dp[n][pos]>0) {
for(int i=n,now=pos;i>=1;now=memo[i][now][0],--i) ans[i]=memo[i][now][1]+'0';
return 1;
}
return 0;
}
int main() {
n=read(9),m=read(9),scanf("%s",ori+1);
rep(i,1,m) scanf("%s",s+1),Value=read(9),Insert();
GetFail();
double l=0,r=log2(1e9+7),mid; // 因为 V_i<=1e9,所以其集合平均数也在这个范围
while(r-l>eps) {
mid=(l+r)/2;
if(ok(mid)) l=mid;
else r=mid;
}
ok(l); puts(ans+1);
return 0;
}