码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 3864: Hero meet devil(dp套dp)

时间:2019-03-14 22:25:28      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:using   return   efi   ++   cpp   include   n+1   amp   clu   

题面

给你一个只由\(AGCT\)组成的字符串\(S (|S| ≤ 15)\),对于每个\(0 ≤ .. ≤ |S|\),问
有多少个只由\(AGCT\)组成的长度为\(m(1 ≤ m ≤ 1000)\)的字符串\(T\),使得\(LCS(T,S)=i\)

题解

老早就听说这个叫做\(dp\ of\ dp\)的神仙了……然而一直没学……

我们先考虑\(LCS\)是怎么转移的,设\(LCS(i,j)\)表示第一个串到\(i\),第二个串到\(j\)为止的最长公共子序列,那么转移为

\[ LCS(i,j)=\max \begin{cases} LCS(i-1,j-1)+1 &S[i]=T[j]\LCS(i,j-1)\LCS(i-1,j) \end{cases} \]

然后我们发现\(LCS(i,j)\)的值和\(LCS(i,j-1)\)的值相差最多不会超过\(1\)

那么我们把数组的第二维差分一下,再状压成一个二进制序列,然后我们就可以预处理出\(to[s][k]\)表示当前\(LCS\)状态为\(s\),加的下一个字符为\(k\),可以到达的\(LCS\)状态是什么

然后设\(f[i][s]\)表示当前在第\(i\)个位置,此时\(LCS\)状态为\(s\)的方案数

那么转移方程就是

\[f[i][to[s][k]]+=f[i-1][s](k=A,T,G,C)\]

边界为\(f[0][0]=1\)

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
const char T[15]={"ATGC"};const int N=(1<<15)+5,P=1e9+7;
inline void Add(R int &x,R int y){(x+=y)>=P?x-=P:0;}
char S[19];int to[N][4],sz[N],f[2][N],ans[1005];
int n,m,lim,t;
void init(){
    static int d[19],g[19];
    fp(s,0,lim-1){
        sz[s]=sz[s>>1]+(s&1);
        fp(j,0,n-1)d[j+1]=d[j]+(s>>j&1);
        fp(k,0,3){
            fp(j,1,n){
                g[j]=max(g[j-1],d[j]);
                T[k]==S[j]?cmax(g[j],d[j-1]+1):0;
            }
            to[s][k]=0;
            fp(j,0,n-1)g[j+1]-g[j]?(to[s][k]|=(1<<j)):0;
        }
    }
}
int main(){
//  freopen("testdata.in","r",stdin);
    int cas;scanf("%d",&cas);
    while(cas--){
        scanf("%s%d",S+1,&m),n=strlen(S+1),lim=(1<<n);
        init(),memset(ans,0,4*(n+1)),memset(f,0,sizeof(f));
        f[0][0]=1,t=0;
        for(R int i=1;i<=m;++i,t^=1){
            memset(f[t^1],0,4*lim);
            fp(s,0,lim-1)fp(k,0,3)Add(f[t^1][to[s][k]],f[t][s]);
        }
        fp(s,0,lim-1)Add(ans[sz[s]],f[t][s]);
        fp(i,0,n)printf("%d\n",ans[i]);
    }
    return 0;
}

bzoj 3864: Hero meet devil(dp套dp)

标签:using   return   efi   ++   cpp   include   n+1   amp   clu   

原文地址:https://www.cnblogs.com/bztMinamoto/p/10534010.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!