zoukankan      html  css  js  c++  java
  • P5319[BJOI2019]奥术神杖【0/1分数规划,AC自动机,dp】

    正题

    题目链接:https://www.luogu.com.cn/problem/P5319


    题目大意

    一个长度为\(n\)的串\(T\),用\(0\sim 9\)填充所有的\(.\)

    然后给出\(m\)个串和它们的价值。

    一个填充方案的价值等于:若\(T\)中出现了\(c\)个给出的串,那价值等于它们的价值乘积开\(c\)次根。

    \(1\leq m\leq 1501,1\leq V_i\leq 10^9\)


    解题思路

    \[ans=\sqrt[c]{\prod V_i} \]

    \[\ln ans=\frac{1}{c}\sum (\ln V_i) \]

    然后就是一个\(0/1\)分数规划问题了,因为要匹配,所以要先跑一个\(AC\)自动机上\(dp\)就好了。

    时间复杂度\(O(n^2\log 10^9)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    using namespace std;
    const int N=1510;
    const double eps=1e-6;
    int n,m,cnt,ch[N][10],fail[N],num[N];
    double w[N],v[N],f[N][N];
    pair<int,int>pre[N][N];queue<int> q;
    char T[N],S[N];
    void Insert(char *s,double val){
    	int l=strlen(s),x=0;
    	for(int i=0;i<l;i++){
    		int c=s[i]-'0';
    		if(!ch[x][c])ch[x][c]=++cnt;
    		x=ch[x][c];
    	}
    	v[x]+=val;num[x]++;
    	return;
    }
    void Build(){
    	for(int i=0;i<10;i++)
    		if(ch[0][i])q.push(ch[0][i]);
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		v[x]+=v[fail[x]];num[x]+=num[fail[x]];
    		for(int i=0;i<10;i++){
    			if(!ch[x][i])ch[x][i]=ch[fail[x]][i];
    			else{
    				fail[ch[x][i]]=ch[fail[x]][i];
    				q.push(ch[x][i]);
    			}
    		}
    	}
    	return;
    }
    int check(double mid){
    	for(int i=0;i<=cnt;i++)w[i]=v[i]-mid*num[i];
    	for(int i=0;i<=n;i++)
    		for(int j=0;j<=cnt;j++)
    			f[i][j]=-1e100;
    	f[0][0]=0;
    	for(int i=1;i<=n;i++){
    		for(int j=0;j<=cnt;j++){
    			if(f[i-1][j]<=-1e99)continue;
    			if(T[i]=='.'){
    				for(int k=0;k<10;k++){
    					int y=ch[j][k];
    					if(f[i][y]<f[i-1][j]+w[y])
    						f[i][y]=f[i-1][j]+w[y],pre[i][y].first=j,pre[i][y].second=k;
    				}
    			}
    			else{
    				int k=T[i]-'0',y=ch[j][k];
    				if(f[i][y]<f[i-1][j]+w[y])
    					f[i][y]=f[i-1][j]+w[y],pre[i][y].first=j,pre[i][y].second=k;
    			}
    		}
    	}
    	int root=0;
    	for(int j=0;j<=cnt;j++)
    		if(f[n][j]>f[n][root])root=j;
    	return root;
    }
    void print(int k,int x){
    	if(k<1)return; 
    	print(k-1,pre[k][x].first);
    	printf("%d",pre[k][x].second);
    	return;
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	scanf("%s",T+1);
    	for(int i=1;i<=m;i++){
    		int x;scanf("%s",S);
    		scanf("%d",&x);
    		Insert(S,log(x));
    	}
    	Build();
    	double l=0,r=21;
    	while(r-l>eps){
    		double mid=(l+r)/2.0;
    		int root=check(mid);
    		if(f[n][root]>eps)l=mid;
    		else r=mid;
    	}
    	int root=check(l);
    	print(n,root);
    	return 0;
    }
    
  • 相关阅读:
    N、Z、Q、R 分别代表什么
    Android常用代码-监听网络状态
    完整的android use SSL发送邮件
    android 发送邮件相关文章
    Spring RMI的实现原理
    spring+quartz配置
    Quartz配置表达式
    singleton容器
    Spring学习-框架概览
    [Shader2D]漩涡效果
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14405563.html
Copyright © 2011-2022 走看看