• [BJOI2019]奥术神杖——AC自动机+DP+分数规划+二分答案


    题目链接:

    [BJOI2019]奥术神杖

    答案是$ans=sqrt[c]{prod_{i=1}^{c}v_{i}}=(prod_{i=1}^{c}v_{i})^{frac{1}{c}}$。

    这样不大好求,我们将这个式子取$ln$,变成$ln ans=frac{1}{c}sum_{i=1}^{c}ln v_{i}$。

    这显然是一个分数规划,每次二分一个答案$mid$,将每个串的权值都减去$mid$,那么只需要求最大价值是否大于$0$即可。

    剩下的问题就是一个在$AC$自动机上的$DP$了,设$f[i][j]$表示在$AC$自动机上的点$j$,已经匹配的长度为$i$时的最大值,在$AC$自动机上转移即可。

    在$DP$时还要记录一下每个状态从哪个点转移过来以便输出方案。

    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const double eps=1e-5;
    int end[2000];
    double f[2000][2000];
    int g[2000][2000];
    int fail[2000];
    int tr[2000][10];
    int cnt;
    double val[2000];
    char ch[2000];
    char s[2000];
    int now;
    int n,m;
    int v;
    double ans;
    void build(char *ch,int v)
    {
    	int len=strlen(ch);
    	now=0;
    	for(int i=0;i<len;i++)
    	{
    		int x=ch[i]-'0';
    		if(!tr[now][x])
    		{
    			tr[now][x]=++cnt;
    		}
    		now=tr[now][x];
    	}
    	end[now]++;
    	val[now]+=log(v);
    }
    void get_fail()
    {
    	queue<int>q;
    	for(int i=0;i<=9;i++)
    	{
    		if(tr[0][i])
    		{
    			q.push(tr[0][i]);
    		}
    	}
    	while(!q.empty())
    	{
    		now=q.front();
    		q.pop();
    		val[now]+=val[fail[now]];
    		end[now]+=end[fail[now]];
    		for(int i=0;i<=9;i++)
    		{
    			if(tr[now][i])
    			{
    				fail[tr[now][i]]=tr[fail[now]][i];
    				q.push(tr[now][i]);
    			}
    			else
    			{
    				tr[now][i]=tr[fail[now]][i];
    			}
    		}
    	}
    }
    double DP(double mid)
    {
    	for(int i=0;i<=n;i++)
    	{
    		for(int j=0;j<=cnt;j++)
    		{
    			f[i][j]=-1e9;
    			g[i][j]=0;
    		}
    	}
    	f[0][0]=0;
    	for(int i=0;i<n;i++)
    	{
    		for(int j=0;j<=cnt;j++)
    		{
    			if(f[i][j]>-1e9)
    			{
    				if(s[i+1]=='.')
    				{
    					for(int k=0;k<=9;k++)
    					{
    						if(f[i+1][tr[j][k]]<f[i][j]+val[tr[j][k]]-mid*end[tr[j][k]])
    						{
    							f[i+1][tr[j][k]]=f[i][j]+val[tr[j][k]]-mid*end[tr[j][k]];
    							g[i+1][tr[j][k]]=j;
    						}
    					}
    				}
    				else
    				{
    					int x=s[i+1]-'0';
    					if(f[i+1][tr[j][x]]<f[i][j]+val[tr[j][x]]-mid*end[tr[j][x]])
    					{
    						f[i+1][tr[j][x]]=f[i][j]+val[tr[j][x]]-mid*end[tr[j][x]];
    						g[i+1][tr[j][x]]=j;
    					}
    				}
    			}
    		}
    	}
    	double res=-1e9;
    	for(int i=0;i<=cnt;i++)
    	{
    		res=max(res,f[n][i]);
    	}
    	return res;
    }
    void print(int dep,int now)
    {
    	if(!dep)
    	{
    		return ;
    	}
    	print(dep-1,g[dep][now]);
    	if(s[dep]!='.')
    	{
    		printf("%c",s[dep]);
    		return ;
    	}
    	else
    	{
    		for(int i=0;i<=9;i++)
    		{
    			if(tr[g[dep][now]][i]==now)
    			{
    				printf("%c",i+'0');
    				return ;
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	scanf("%s",s+1);
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%s%d",ch,&v);
    		build(ch,v);
    	}
    	get_fail();
    	double l=0,r=22;
    	while(r-l>eps)
    	{
    		double mid=(l+r)/2;
    		if(DP(mid)>0)
    		{
    			l=mid;
    			ans=mid;
    		}
    		else
    		{
    			r=mid;
    		}
    	}
    	DP(ans);
    	for(int i=0;i<=cnt;i++)
    	{
    		if(f[n][i]>0)
    		{
    			print(n,i);
    			break;
    		}
    	}
    }
  • 相关阅读:
    Java基础(九)
    Java基础(八)
    Java基础(七)
    Java基础(六)
    MyBatis的一级缓存和二级缓存
    安卓音频处理相关资料集合贴
    Android studio下gradle Robolectric单元测试配置
    【翻译】安卓新播放器EXOplayer介绍
    ActionBar compat 如何禁用ActionBar的显示/隐藏动画
    如何实现自定义的android WebView错误页
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10772191.html
Copyright © 2020-2023  润新知