• bzoj 4606: [Apio2008]DNA【dp】


    写题五分钟读题两小时系列……
    看懂题的话不算难,然而我去看了大佬的blog才看懂题……
    题目大意是:一个原字符串,其中有一种通配符,合法串的定义是这个串(不含通配符))可以匹配原串并且这个串最多分成k段就能使每一段字典序单调不降。求在所有合法串中字典序第r大的。
    设f[i][j][k]表示第i个字符选j,至少需要分成k段的方案数,倒着dp,特判一下通配符,比较基础就不多说了
    然后对于每个f[i][j],把k维变成前缀和的形式,因为能分为k段就能分为k+1段。(这里其实当i<k的时候是不对的,但是我写的时候没有特判也过了就没改= =)
    然后正着算答案,有点像splay上求k大数的感觉,就是一边匹配一边把r减去当前方案之前(字典序小于当前方案)的方案数。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=50005;
    int n,m,a[N],h[305];
    long long r,ans,f[N][5][15];
    char s[N],b[5];
    int main()
    {
    	h['A']=0,h['C']=1,h['G']=2,h['T']=3,h['N']=4;
    	b[0]='A',b[1]='C',b[2]='G',b[3]='T';
    	scanf("%d%d%lld%s",&n,&m,&r,s+1);
    	for(int i=1;i<=n;i++)
    		a[i]=h[s[i]];
    	if(a[n]==4)
    		f[n][0][1]=1,f[n][1][1]=1,f[n][2][1]=1,f[n][3][1]=1;
    	else
    		f[n][a[n]][1]=1;
    	for(int i=n-1;i>=1;i--)
    	{
    		if(a[i]==4)
    		{
    			for(int j=0;j<=3;j++)
    				for(int k=1;k<=m;k++)
    					for(int l=0;l<=3;l++)
    						f[i][j][k]+=f[i+1][l][k-(j>l)];
    		}
    		else
    		{
    			for(int k=1;k<=m;k++)
    					for(int l=0;l<=3;l++)
    						f[i][a[i]][k]+=f[i+1][l][k-(a[i]>l)];
    		}
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<=3;j++)
    			for(int k=1;k<=m;k++)
    				f[i][j][k]+=f[i][j][k-1];
    	for(int i=1,j;i<=n;i++)
    	{
    		for(j=0;j<=3;j++)
    		{
    			long long sum;
    			if(j<a[i-1])
    				sum=f[i][j][m-1];
    			else
    				sum=f[i][j][m];
    			if(r>sum)
    				r-=sum;
    			else
    				break;
    		}
    		a[i]=j;
    		if(a[i]<a[i-1])
    			m--;
    		printf("%c",b[j]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    关于日志造成的频繁的IO
    PHP
    gitignore
    Linux安装gradle
    Ambari和ClouderManager分析对比
    原生hadoop生态系统组件安装文档
    hive的数据类型和数据模型
    hive概述
    使用binlog和canal从mysql实时抽取数据
    canal概述
  • 原文地址:https://www.cnblogs.com/lokiii/p/8823971.html
Copyright © 2020-2023  润新知