• 题解 [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时的最大贡献,转移显然。

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

    ( exttt{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;
    }
    
  • 相关阅读:
    set
    皮肤病药物
    C 批量保存图片进 mysql 利用MYSQL_BIND插入longblob
    eclipse行号显示
    dynamic_cast使用
    list添加删除操作
    VS2008 对齐代码
    c++ mysql二进制存取,blob存取
    Select Window关键字——模拟打开了多个页面窗口时,在不同的窗口之间,进行窗口切换
    Click Image关键字——模拟单击某一个图片 其余:Click Button / Click Link
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13700950.html
Copyright © 2020-2023  润新知