bzoj4572: [Scoi2016]围棋

传送门

n==2的点随便怎么dp一下就好了。

然后考虑正经的做法,无脑暴力枚举上下两行的状态的话是n*m*3^m*3^m

容易想到把上一行用二进制表示以第i个数结尾能不能完全匹配第一行的串,这样大概有n*m*2^m*3^m

好像有60了吧。

如果可以只保存上一行的状态和这一行的匹配情况岂不是美滋滋,但是这样我无法向下一行转移啊。

于是就咕咕咕了。

考虑上一行的二进制串,这一行匹配到第j位时前j位都没有用了,那我岂不是可以直接把这一行的压上去!

没错就是轮廓线dp啊。

于是就做完了。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<ctime>
#include<cmath>
const int mod=1e9+7;
typedef long long LL;
using namespace std;
int n,m,c,q,nn; 
int dp[101][13][(1<<10)+1][7][7],nx1[10],nx2[10];
char s1[10],s2[10],ss[10]={'W','B','X'};

template<typename T> void read(T &x) {
    char ch=getchar(); x=0; T f=1;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=-1,ch=getchar();
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}

void make_nxt(char s[],int nxt[]) {
    for(int i=1,k=0;i<c;i++) {
        while(k&&s[k]!=s[i]) k=nxt[k-1];
        if(s[k]==s[i]) k++;
        nxt[i]=k;
    }
}

void clear() {
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    for(int k=0;k<=nn;k++)
    for(int x=0;x<=c;x++)
    for(int y=0;y<=c;y++) 
    dp[i][j][k][x][y]=0; 
}

void add(int &x,int y) { LL res=x; res+=y; if(res>=mod) res-=mod; x=res; }

int main() {
    read(n); read(m); read(c); read(q);
    LL power=1;
    for(int i=1;i<=n*m;i++) (power*=3)%=mod;
    while(q--) {
        scanf("%s%s",s1,s2);
        make_nxt(s1,nx1); make_nxt(s2,nx2);
        nn=(1<<(m-c+1))-1;
        dp[0][m][0][0][0]=1;
        LL ans=0;
        for(int i=0;i<=n;i++) {
            for(int j=(i==0?m:1);j<=m;j++) {
                for(int s=0;s<=nn;s++) {
                    for(int x=0;x<=c;x++)
                    for(int y=0;y<=c;y++) if(dp[i][j][s][x][y]) {
                        if(i==n&&j==m) (ans+=dp[i][j][s][x][y])%=mod;
                        int ni,nj,nx,ny,ns;
                        for(int k=0;k<3;k++) {
                            if(j==m) ni=i+1,nj=1,nx=0,ny=0;
                            else ni=i,nj=j+1,nx=x,ny=y; ns=s;
                            while(nx&&ss[k]!=s1[nx]) nx=nx1[nx-1];
                            if(ss[k]==s1[nx]) nx++;
                            while(ny&&ss[k]!=s2[ny]) ny=nx2[ny-1];
                            if(ss[k]==s2[ny]) ny++;
                            if(ny==c&&(s&(1<<nj-c))) continue;
                            if(nx==c&&(!(ns&(1<<nj-c)))) ns|=(1<<nj-c);
                            else if(nx!=c&&(ns&(1<<nj-c))) ns^=(1<<nj-c);
                            if(ni>n||nj>m) continue;
                            add(dp[ni][nj][ns][nx][ny],dp[i][j][s][x][y]);
                        }
                    }
                }
            }
        }
        ans=(power-ans+mod)%mod;
        printf("%lld\n",ans);
        if(q) clear();
    }
    return 0;
}
/*
3 3 2 3
XB
BW
BX
XB
BB
BB
*/
View Code

 

posted @ 2018-02-01 17:01  啊宸  阅读(369)  评论(0编辑  收藏  举报