BJOI2019 奥术神杖

传送门

首先 w 1 w 2 w 3 ⋯ w n n \sqrt[n]{w_1w_2w_3\cdots w_n} nw1w2w3wn 可以利用对数转化:
l o g 2 w 1 w 2 w 3 ⋯ w n n = 1 n ∑ l o g 2   w i log_2{\sqrt[n]{w_1w_2w_3\cdots w_n}}=\frac{1}{n}\sum log_2\ w_i log2nw1w2w3wn =n1log2 wi
那么令 t i = l o g 2   w i t_i=log_2\ w_i ti=log2 wi,问题就转化为了:
m a x i m i z e : ∑ t i ∑ 1 maximize:\frac{\sum{t_i}}{\sum{1}} maximize1ti
也就是一个分数规划:二分 λ \lambda λ,权值转化为 t i − λ t_i-\lambda tiλ。由于求最大值,那么:
如果最大的和小于 0 0 0,说明 λ > a n s \lambda>ans λ>ans,则 r = m i d r=mid r=mid。(减的多了)
如果最大的和大于 0 0 0,说明 λ < a n s \lambdaλ<ans,则 l = m i d l=mid l=mid。(减的少了)

A C AC AC自动机上 D P DP DP出最大值即可。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示当前填到了原串的第 i i i位,处于自动机上的第 j j j位的和的最大值。

#include
#define re register
#define cs const
cs int N=2010,maxS=2010;
cs double eps=1e-5;
int n,m;char S[N],spell[maxS];double W;
namespace AC{
	cs int alpha=10;
	int Pre[N][maxS],Ch[N][maxS],tot=0;double dp[N][maxS];
	struct node{int son[alpha],fail,num;double val;}a[maxS];
	inline void insert(char *s,double v){
		int now=0,len=strlen(s);
		for(int re i=0;i<len;++i){
			if(!a[now].son[s[i]-'0'])
				a[now].son[s[i]-'0']=++tot;
			now=a[now].son[s[i]-'0'];
		}a[now].val=v,a[now].num+=1;
	}
	inline void build_fail(){
		std::queue<int> Q;
		for(int re i=0,v;i<alpha;++i)
			if(v=a[0].son[i]) Q.push(v),a[v].fail=0;
		while(!Q.empty()){
			int u=Q.front();Q.pop();
			a[u].num+=a[a[u].fail].num;
			a[u].val+=a[a[u].fail].val;
			for(int re i=0,v;i<alpha;++i){
				if(v=a[u].son[i]) a[v].fail=a[a[u].fail].son[i],Q.push(v);
				else a[u].son[i]=a[a[u].fail].son[i];
			}
		}
	}
	inline bool check(double x){
		memset(dp,-0x3f,sizeof dp);dp[0][0]=0;
		for(int re i=0;i<n;++i){
			for(int re j=0;j<=tot;++j)
				if(S[i+1]=='.')
					for(int re v=0,k=a[j].son[v];v<alpha;k=a[j].son[++v]){
						double calc=dp[i][j]+a[k].val-x*a[k].num;
						if(dp[i+1][k]<calc) dp[i+1][k]=calc,Pre[i+1][k]=j,Ch[i+1][k]=v;
					}
				else{
					int k=a[j].son[S[i+1]-'0'];double calc=dp[i][j]+a[k].val-x*a[k].num;
					if(dp[i+1][k]<calc) dp[i+1][k]=calc,Pre[i+1][k]=j;
				}
		}
		for(int i=0;i<=tot;++i) if(dp[n][i]>0) return true;
		return false;
	}
	inline void print(int l,int pos){
		if(!l) return;print(l-1,Pre[l][pos]);
		putchar((S[l]=='.')?(Ch[l][pos]+'0'):(S[l]));
	}
	inline void solve(){
		double l=0.0,r=30;
		while(r-l>eps){
			double mid=(l+r)/2;
			if(check(mid)) l=mid;
			else r=mid;
		}check(l);
		for(int i=0;i<=tot;++i)
			if(dp[n][i]>0){print(n,i);return;}
	}
}
using AC::insert;
int main(){
//	freopen("4505.in","r",stdin);
	scanf("%d%d%s",&n,&m,S+1);
	while(m--) scanf("%s",spell),scanf("%lf",&W),W=log(W),insert(spell,W);
	AC::build_fail();return AC::solve(),0;
}

你可能感兴趣的:(BJOI2019 奥术神杖)