Live2D

题解 [BJOI2019]奥术神杖

题目传送门

题目大意

给出一个残缺的字符串,每个位置都 \(\in[0,9]\)。有 \(m\) 中贡献,即 \(s,k\),表示该字符串中没出现一次 \(s\),贡献便乘上 \(k\)。最后对贡献求 \(c\) 次根,其中 \(c\) 是总出现次数。求贡献的最大值。

字符串长度以及贡献字符串长度之和 \(\le 1500\)

思路

首先你需要想到我们可以全部取 \(\ln\),然后每次贡献就是 \(+k\),求根就是 \(/c\),于是问题就是最大化:

\[\frac{\sum k}{c} \]

然后你对这个二分,判断条件就是:

\[\sum_{k-mid}>0 \]

于是我们可以在 AC 自动机上进行dp,即设 \(f_{i,j}\) 表示到第 \(i\) 个字符串对应自动机上状态j时的最大贡献,转移显然。

于是,我们就可以在 \(nl\log w\) 的时间复杂度内解决这个问题。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 2005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

int n,m;
int g[MAXN][MAXN][2];
double dp[MAXN][MAXN];
char s1[MAXN],s2[MAXN],ans[MAXN];

struct Auto{
	double val[MAXN];
	int cnt,ch[MAXN][10],fail[MAXN],sum[MAXN];
	void Insert (char *s,double v){
		int now = 0;
		for (Int i = 1;s[i];++ i){
			if (!ch[now][s[i] - '0']) ch[now][s[i] - '0'] = ++ cnt;
			now = ch[now][s[i] - '0'];
		}
		sum[now] ++,val[now] += v;
	}
	void Build (){
		queue <int> q;
		while (!q.empty()) q.pop ();
		for (Int i = 0;i < 10;++ i) if (ch[0][i]) q.push (ch[0][i]);
		while (!q.empty()){
			int u = q.front();q.pop ();
			sum[u] += sum[fail[u]],val[u] += val[fail[u]];
			for (Int i = 0;i < 10;++ i){
				if (ch[u][i]) fail[ch[u][i]] = ch[fail[u]][i],q.push (ch[u][i]);
				else ch[u][i] = ch[fail[u]][i];
			}
		} 
	}
	double Work (double v){
		for (Int i = 0;i <= cnt;++ i) val[i] -= sum[i] * v;
		for (Int i = 0;i <= n;++ i) for (Int j = 0;j <= cnt;++ j) dp[i][j] = -1e6;
		dp[0][0] = 0;
		for (Int i = 0;i < n;++ i) 
			for (Int j = 0;j <= cnt;++ j)
				if (dp[i][j] > -1e5)
					for (Int k = 0;k < 10;++ k)
						if (s1[i] == '.' || s1[i] == k + '0'){
							int t = ch[j][k];
							if (dp[i + 1][t] < dp[i][j] + val[t]){
								dp[i + 1][t] = dp[i][j] + val[t];
								g[i + 1][t][0] = k,g[i + 1][t][1] = j;
							}
						}
		for (Int i = 0;i <= cnt;++ i) val[i] += sum[i] * v;
		int pos = 0;for (Int i = 0;i <= cnt;++ i) if (dp[n][i] > dp[n][pos]) pos = i;
		for (Int i = n,now = pos;i;-- i) ans[i] = g[i][now][0] + '0',now = g[i][now][1];
		return dp[n][pos];
	}
}T;

signed main(){
	read (n,m);
	scanf ("%s",s1);
	for (Int i = 1,v;i <= m;++ i){
		scanf ("%s",s2 + 1),read (v);
		T.Insert (s2,log (v)); 
	}
	T.Build(); 
	double l = 0,r = 1e9;
	while (r - l > 1e-3){
		double mid = (l + r) / 2;
		if (T.Work(mid) > 0) l = mid;
		else r = mid; 
	}
	T.Work(l),printf ("%s",ans + 1);
	return 0;
}
posted @ 2020-09-20 16:52  Dark_Romance  阅读(161)  评论(0编辑  收藏  举报