[BJOI2019]奥术神杖(分数规划,动态规划,AC自动机)

[BJOI2019]奥术神杖(分数规划,动态规划,AC自动机)

题面

洛谷

题解

首先乘法取\(log\)变加法,开\(c\)次根变成除\(c\)
于是问题等价于最大化\(\displaystyle \frac{\sum val_i}{c}\)。典型的分数规划的形式。
二分权值\(k\),每个点的点权变成\(val_i-k\),转为求最值,那么直接在\(AC\)自动机上\(dp\)就行了。
注意精度问题。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define MAX 1505
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Node{int son[10],ff,s;double w;}t[MAX];
int tot;
void Insert(char *s,int val)
{
	int l=strlen(s+1),u=0;
	for(int i=1;i<=l;++i)
	{
		int c=s[i]-48;
		if(!t[u].son[c])t[u].son[c]=++tot;
		u=t[u].son[c];
	}
	t[u].w=log(val);t[u].s+=1;
}
int Q[MAX],L,R;
void BuildFail()
{
	L=1;
	for(int i=0;i<10;++i)if(t[0].son[i])Q[++R]=t[0].son[i];
	while(L<=R)
	{
		int u=Q[L++];t[u].w+=t[t[u].ff].w;t[u].s+=t[t[u].ff].s;
		for(int i=0;i<10;++i)
			if(t[u].son[i])t[t[u].son[i]].ff=t[t[u].ff].son[i],Q[++R]=t[u].son[i];
			else t[u].son[i]=t[t[u].ff].son[i];
	}
}
char T[MAX],S[MAX];int n,m;
double f[MAX][MAX];int g1[MAX][MAX],g2[MAX][MAX];
void Tr(int i,int j,int k)
{
	int v=t[j].son[k];
	if(f[i][v]<f[i-1][j]+t[v].w)
	{
		f[i][v]=f[i-1][j]+t[v].w;
		g1[i][v]=j;g2[i][v]=k;
	}
}
bool check(double K)
{
	for(int i=0;i<=tot;++i)t[i].w-=K*t[i].s;
	int len=strlen(T+1);
	for(int i=0;i<=len;++i)
		for(int j=0;j<=tot;++j)f[i][j]=-1e300;
	f[0][0]=0;
	for(int i=1;i<=len;++i)
		for(int j=0;j<=tot;++j)
				if(T[i]=='.')for(int k=0;k<10;++k)Tr(i,j,k);
				else Tr(i,j,T[i]-48);
	double ans=-1e300;
	for(int i=1;i<=tot;++i)ans=max(ans,f[len][i]);
	for(int i=0;i<=tot;++i)t[i].w+=K*t[i].s;
	return ans>0;
}
int main()
{
	n=read();m=read();
	scanf("%s",T+1);
	for(int i=1,v;i<=m;++i)scanf("%s",S+1),v=read(),Insert(S,v);
	BuildFail();
	double l=0,r=21;
	while(r-l>1e-3)
	{
		double mid=(l+r)/2;
		if(check(mid))l=mid;
		else r=mid;
	}
	check(l);int pos=0,len=strlen(T+1);
	for(int i=1;i<=tot;++i)if(f[len][i]>f[len][pos])pos=i;
	for(int i=len;i;--i)S[i]=g2[i][pos]+48,pos=g1[i][pos];
	for(int i=1;i<=len;++i)putchar(S[i]);puts("");
	return 0;
}
posted @ 2019-04-21 19:17  小蒟蒻yyb  阅读(527)  评论(3编辑  收藏  举报