\(\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;
}
posted on 2020-12-23 23:11  Oxide  阅读(95)  评论(0编辑  收藏  举报