bzoj千题计划241:bzoj3864: Hero meet devil

Posted 日拱一卒 功不唐捐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj千题计划241:bzoj3864: Hero meet devil相关的知识,希望对你有一定的参考价值。

http://www.lydsy.com/JudgeOnline/problem.php?id=3864

 

题意:

给你一个DNA序列,求有多少个长度为m的DNA序列和给定序列的LCS为0,1,2....

 

求LCS方式:f[i][j]=max(f[i-1][j],f[i][j-1],f[i-1][j-1]*(s[i]==t[j]))

固定了i,相邻的j的f[i][j]值最多相差1

dp[i][j] 表示长度为i的DNA序列,将“f[ |S| ][j+1]是否比f[ |S| ][j] 大1” 这个状态压缩为j的方案数

若我们知道 状态j加上一个字母k可以到状态nxt[j][k]

那么dp[i+1][nxt[j][k]]+=dp[i][j]

 

关键是如何求得nxt[j][k]

再一次DP

枚举所有的状态i

令f[j] 表示加上字母k之前的LCS长度,g[j]表示加上字母k之后的LCS长度

g[j]=max(g[j-1],f[j])

如果加上的字母k和原序列第j个字母匹配 g[i]=max(g[j],f[j-1]+1)

g求完后,项邻的两个g要么相等,要么相差1

再把这个状态压缩起来即可

 

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int mod=1e9+7;

int m;

char ss[16];
int L,S;
int s[16];

int ch[26];

int f[16],g[16];
int nxt[1<<15][4];

int dp[2][1<<15];
int ans[16];

void pre()
{
    int len; int c[16];
    for(int i=0;i<S;++i)
    {
        memset(f,0,sizeof(f));
        for(int j=1;j<=L;++j) f[j]=f[j-1]+(i>>j-1&1);
        for(int k=0;k<4;++k)
        {
            for(int j=1;j<=L;++j)
            {
                g[j]=max(g[j-1],f[j]);
                if(s[j]==k) g[j]=max(g[j],f[j-1]+1);
            }
            nxt[i][k]=0;
            for(int j=0;j<L;++j)
                if(g[j+1]-g[j]) nxt[i][k]+=1<<j;
        }
    }
}

int count(int x)
{
    int sum=0;
    while(x)
    {
        sum+=x&1;
        x>>=1;
    }
    return sum;
}

void DP()
{
    memset(dp,0,sizeof(dp));
    int now=1,last=0;
    dp[0][0]=1;
    for(int i=1;i<=m;++i)
    {
        memset(dp[now],0,sizeof(dp[now]));
        for(int j=0;j<S;++j)
            for(int k=0;k<4;++k)
            {
                dp[now][nxt[j][k]]+=dp[last][j];
                dp[now][nxt[j][k]]-=dp[now][nxt[j][k]]>=mod ? mod : 0;
            }
        swap(now,last);
    }
    memset(ans,0,sizeof(ans));
    int t;
    for(int i=0;i<S;++i) 
    {
        t=count(i);
        ans[t]+=dp[last][i];
        ans[t]-=ans[t]>=mod ? mod : 0;
    }
    for(int i=0;i<=L;++i) printf("%d\n",ans[i]);
}

int main()
{
    ch[A-A]=0;
    ch[C-A]=1;
    ch[G-A]=2;
    ch[T-A]=3;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",ss+1);
        scanf("%d",&m);
        L=strlen(ss+1);
        S=1<<L;
        for(int i=1;i<=L;++i) s[i]=ch[ss[i]-A];
        pre();
        DP();
    }
    return 0;
}

 

以上是关于bzoj千题计划241:bzoj3864: Hero meet devil的主要内容,如果未能解决你的问题,请参考以下文章

bzoj千题计划197:bzoj4247: 挂饰

bzoj千题计划118:bzoj1028: [JSOI2007]麻将

bzoj千题计划165:bzoj5127: 数据校验

bzoj千题计划144:bzoj1176: [Balkan2007]Mokia

bzoj千题计划177:bzoj1858: [Scoi2010]序列操作

bzoj千题计划142:bzoj3144: [Hnoi2013]切糕