\(\text{Description}\)
\(\text{Solution}\)
发现有 \(ans=\sqrt[c]{\prod_{i=1}^c v_i}\)。不过显然这个东西直接算出来会爆。
有一个操作就是同时取对数。就有:
\[\ln ans=\ln\left(\sqrt[c]{\prod_{i=1}^c v_i}\right )
\]
\[\ln ans=\frac{1}{c} \ln\left(\prod_{i=1}^c v_i\right )
\]
\[\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
\]
\[c\cdot mid<\sum_{i=1}^c \ln v_i
\]
\[\sum_{i=1}^c \left(\ln v_i-mid\right)>0
\]
我们可以对于每个 \(mid\),求出 \(\sum_{i=1}^c \left(\ln v_i-mid\right)\) 的最大值(这里的所有 \(v\) 显然是自己选定)。如果满足最大值 \(>0\),说明 \(mid\) 还可以取到更大(带回原来的柿子看)。
对于求出 \(\sum_{i=1}^c \left(\ln v_i-mid\right)\) 的最大值,显然就是将单个咒语权值减 \(mid\),用 \(\text{AC}\) 自动姬统计某个后缀出现的权值(这个后缀可能包含很多咒语),然后大力 \(\text{DP}\)。
设 \(f[i][j]\) 为到神杖的第 \(i\) 位,在 \(\text{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\) 对应的节点。
\(\text{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;
}