[BJOI2019]奥术神杖
我要是生在\(bj\)估计就凉了,一道\(d1t1\)做了\(4h+\)
首先看到这个鬼畜的计算贡献的柿子
\[\prod_{i=1}^na_i^{\frac{b_i}{\sum_{j=1}^nb_j}}
\]
\(a_i\)为第\(i\)个字符串的价值,\(b_i\)为第\(i\)个字符串出现的次数
我们显然需要取一个\(\ln\)
就变成了
\[\sum_{i=1}^n\frac{b_i}{\sum_{j=1}^nb_j}\ln a_i
\]
这样写太难看了
我们随便一些就变成了
\[\frac{\sum_{i=1}^nb_i\ln a_i}{\sum_{i=1}^nb_i}
\]
一看这不分数规划吗,我们直接二分一个\(mid\)看看答案是否能更大就好了
如果更大
\[\frac{\sum_{i=1}^nb_i\ln a_i}{\sum_{i=1}^nb_i}>mid
\]
也就是
\[\sum_{i=1}^nb_i(\ln a_i-mid)>0
\]
显然这又是一个多串匹配的问题,我们直接上\(AC\)自动机
现在的问题就是在自动机上找到一条长度为\(n\)的路径,点权和最大
直接在自动机上按位\(dp\)就好了,\(dp[i][j]\)表示匹配的长度是\(i\)当前在自动机上的第\(j\)个节点的最大路径长度
代码
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const double eps=1e-12;
const int maxn=1505;
struct E{int v,nxt;}e[maxn];
int n,m,cnt,num,L;
char S[maxn],T[maxn];
int id[maxn],fa[maxn],vis[2][maxn],son[maxn][10];
int r[maxn][maxn],c[maxn][maxn],head[maxn],tmp[maxn];
double val[maxn],d[maxn];
double dp[2][maxn],f[maxn][maxn];
inline void add(int x,int y) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void Dfs(int x) {
for(re int i=head[x];i;i=e[i].nxt)
d[e[i].v]+=d[x],Dfs(e[i].v);
}
inline int pd(double a) {return a+eps>0&&a-eps<0;}
inline void ins(int j) {
int v;
scanf("%s",T+1);scanf("%d",&v);
int now=0,len=strlen(T+1);L+=len;
for(re int i=1;i<=len;i++) {
if(!son[now][T[i]-'0']) son[now][T[i]-'0']=++cnt;
now=son[now][T[i]-'0'];
}
tmp[now]++;id[j]=now;val[j]=log(v);
}
inline void Build() {
std::queue<int> q;
for(re int i=0;i<=9;i++) if(son[0][i]) q.push(son[0][i]);
while(!q.empty()) {
int k=q.front();q.pop();
add(fa[k],k);tmp[k]+=tmp[fa[k]];
for(re int i=0;i<=9;i++)
if(son[k][i]) fa[son[k][i]]=son[fa[k]][i],q.push(son[k][i]);
else son[k][i]=son[fa[k]][i];
}
}
inline void Pre_work(double mid) {
for(re int i=0;i<=cnt;i++) d[i]=0;
for(re int i=1;i<=m;i++) d[id[i]]+=val[i]-mid;
Dfs(0);
}
inline int check(double mid) {
Pre_work(mid);
memset(dp,-20,sizeof(dp));
dp[0][0]=0;int o=0;vis[0][0]=1;
for(re int i=0;i<n;i++,o^=1) {
memset(dp[o^1],-20,sizeof(dp[o^1]));
memset(vis[o^1],0,sizeof(vis[o^1]));
for(re int j=0;j<=cnt;j++) {
if(!vis[o][j]) continue;
if(S[i+1]>='0'&&S[i+1]<='9') {
int v=son[j][S[i+1]-'0'];
vis[o^1][v]=1;
dp[o^1][v]=max(dp[o^1][v],dp[o][j]+d[v]);
continue;
}
for(re int k=0;k<=9;k++) {
int v=son[j][k];
vis[o^1][v]=1;
dp[o^1][v]=max(dp[o^1][v],dp[o][j]+d[v]);
}
}
}
for(re int i=0;i<=cnt;i++)
if(dp[o][i]>0&&!pd(dp[o][1]))
return 1;
return 0;
}
void dfs(int len,int now) {
if(!len) return;
dfs(len-1,c[len][now]);
putchar(r[len][now]+'0');
}
inline void solve(double mid) {
Pre_work(mid);
memset(f,-20,sizeof(f));
f[0][0]=0;
for(re int i=0;i<n;i++)
for(re int j=0;j<=cnt;j++) {
if(S[i+1]>='0'&&S[i+1]<='9') {
int v=son[j][S[i+1]-'0'];
if(f[i][j]+d[v]>f[i+1][v]) {
c[i+1][v]=j;r[i+1][v]=S[i+1]-'0';
f[i+1][v]=f[i][j]+d[v];
}
continue;
}
for(re int k=0;k<=9;k++) {
int v=son[j][k];
if(f[i][j]+d[v]>f[i+1][v]) {
c[i+1][v]=j,r[i+1][v]=k;
f[i+1][v]=f[i][j]+d[v];
}
}
}
int t=0;
for(re int i=1;i<=cnt;i++)
if(f[n][i]>f[n][t]) t=i;
dfs(n,t);
}
namespace sub1 {
char g[15];
double ans=0;
void dfs(int x) {
if(x==n+1) {
double w=0;int y=0,now=0;
for(re int i=1;i<=n;i++) {
now=son[now][S[i]-'0'];
y+=tmp[now];w+=d[now];
}
if(w/(1.0*y)>ans) {
ans=w/(1.0*y);
for(re int i=1;i<=n;i++) g[i]=S[i];
}
return;
}
if(S[x]!='.') {
dfs(x+1);
return;
}
for(re int i=0;i<10;i++) {
S[x]=i+48;
dfs(x+1);
}
S[x]='.';
}
inline void solve() {
Pre_work(0);
dfs(1);
for(re int i=1;i<=n;i++) putchar(g[i]);
}
}
int main() {
scanf("%d%d",&n,&m);
scanf("%s",S+1);
for(re int i=1;i<=m;i++) ins(i);
Build();
if(n<=6&&L<=20) {
sub1::solve();
return 0;
}
double l=0,r=1e5+5,ans=0;
while(r-l>eps) {
double mid=(l+r)/2.0;
if(check(mid)) l=mid,ans=mid;
else r=mid;
}
solve(ans);
return 0;
}