SCOI2016围棋

题目大意

有个n*m的格子图,每个格子上有三种数。
给出q个2*c模版,对于每个模版,求有多少种格子图能够匹配这个模版。

数据范围

这里写图片描述
q 5

直观想法

fi,s 表示现在匹配到第i行,第i行的状态为s(s为三进制数)的方案数。
转移显然。
然后这样的时间复杂度是 O(n×32m) 超时,估计只有20分。

缩减状态

每次计算匹配一个的。
可以发现,由于我们只要匹配一个模版,所以有许多的状态都是无用的,于是我们可以缩减状态。
如何缩减?
对于一个状态,我们关心的只是那个位置匹配了 s0 (或 s1 ,注: s0 表示模板的第一行, s1 表示模版的第二行,以下都这样简称),那么对于每个确定的串,它是有两个二进制状态的,一个二进制位为1当且仅当以当前位为结尾的c为是与模版是一样的,举个例子:
这里写图片描述
然后由于前c-1位都不可能为1(因为一个二进制位为1当且仅当以当前位为结尾的c为是与模版是一样的),所以共有 2mc+1 种状态。
那么转移的东西如图(箭头表示由哪个数组推出哪个数组,可能本人比较蠢,用的数组太多了):
这里写图片描述
转移就很显然了,额如果不明白,看标称吧^_^
这样的时间复杂度就是 O(n×22m2c+2)
空间复杂度为 O(m×c2×22m2c+2)
然后,如果m-c比较大,它就会GG了
很遗憾,数据中真的有这样的数据。。。。。
这里写图片描述
庆幸的是,数据中的这个的n是等于2的,n=2又可以怎样做呢,于是他就可以直接dp就好了,设u[i][w0][w1]表示当前做到第i位,匹配到s0的w0,s1的w1,然后转移显然。。。。。
感觉很水的方法啊啊啊啊啊
说一句:有种水法的感觉
贴个代码哈哈哈哈哈哈:

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)

using namespace std;

typedef long long LL;
typedef double db;

int get(){
    char ch;
    int s=0;
    bool pd=0;
    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
    if (ch=='-')pd=1;
    else s=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
    if (pd)return -s;
    return s;
}

const int N = 110;
const int M = 20;
const int mo = 1e+9+7;
const int zt = 260;

int mi[13],n,m,c,q,len;
int t[M][7][7][zt][zt],g[zt][zt],p[zt],v[zt][zt],w[zt],f[N][zt];
int u[N][7][7];
LL ans;
int s[2][M];
int next[2][M];
LL v3[N*M];

int trans(char ch){
    switch (ch){
    case 'W':return 0;
    case 'B':return 1;
    case 'X':return 2;
    }
}

void init(int v){
    char ch;
    while(ch=getchar(),ch!='B'&&ch!='W'&&ch!='X');
    s[v][1]=trans(ch);
    fo(i,2,c)s[v][i]=trans(getchar());
    int j=0;
    fo(i,2,c){
        while(j&&s[v][j+1]!=s[v][i])j=next[v][j];
        if (s[v][j+1]==s[v][i])j++;
        next[v][i]=j;
    }
}

void work0(){
    fo(ttt,1,q){
        init(0);
        init(1);
        fo(i,0,m)fo(w0,0,c)fo(w1,0,c)u[i][w0][w1]=0;
        u[0][0][0]=1;
        ans=0;
        fo(i,0,m-1)
            fo(w0,0,c-1)
                fo(w1,0,c-1)
                if (u[i][w0][w1]){
                    fo(v0,0,2)
                        fo(v1,0,2){
                            int _w0=w0,_w1=w1;
                            while(_w0&&s[0][_w0+1]!=v0)_w0=next[0][_w0];
                            if (s[0][_w0+1]==v0)_w0++;
                            while(_w1&&s[1][_w1+1]!=v1)_w1=next[1][_w1];
                            if (s[1][_w1+1]==v1)_w1++;
                            if (_w0==c&&_w1==c)
                                ans=(ans+LL(u[i][w0][w1])*v3[2*(m-1-i)]%mo)%mo;
                            else{
                                if (_w0==c)_w0=next[0][c];
                                if (_w1==c)_w1=next[1][c];
                                u[i+1][_w0][_w1]=(u[i+1][_w0][_w1]+u[i][w0][w1])%mo;
                            }
                        }
                }
        printf("%lld\n",ans);
    }
}

void work1(){
    fo(ttt,1,q){
        init(0);
        init(1);
        fo(i,0,m)fo(w0,0,c)fo(w1,0,c)fo(t0,0,mi[len]-1)fo(t1,0,mi[len]-1)
        t[i][w0][w1][t0][t1]=0;
        fo(t0,0,mi[len]-1)fo(t1,0,mi[len]-1)g[t0][t1]=v[t0][t1]=0;
        fo(t0,0,mi[len]-1)p[t0]=w[t0]=0;
        fo(i,0,n)fo(t0,0,mi[len]-1)f[i][t0]=0;
        t[0][0][0][0][0]=1;
        fo(i,0,m-1)
            fo(w0,0,c-1)
                fo(w1,0,c-1)
                    fo(t0,0,mi[len]-1)
                        fo(t1,0,mi[len]-1)
                        if (t[i][w0][w1][t0][t1]){
                            fo(v,0,2){
                                int _w0=w0,_w1=w1,_t0=t0,_t1=t1;
                                while(_w0&&s[0][_w0+1]!=v)_w0=next[0][_w0];
                                if (s[0][_w0+1]==v)_w0++;
                                while(_w1&&s[1][_w1+1]!=v)_w1=next[1][_w1];
                                if (s[1][_w1+1]==v)_w1++;
                                if (_w0==c){
                                    _w0=next[0][c];
                                    _t0|=mi[i+1-c];
                                }
                                if (_w1==c){
                                    _w1=next[1][c];
                                    _t1|=mi[i+1-c];
                                }
                                t[i+1][_w0][_w1][_t0][_t1]=(t[i+1][_w0][_w1][_t0][_t1]+t[i][w0][w1][t0][t1])%mo;
                            }
                        }
        fo(w0,0,c-1)
            fo(w1,0,c-1)
                fo(t0,0,mi[len]-1)
                    fo(t1,0,mi[len]-1){
                        g[t0][t1]=(g[t0][t1]+t[m][w0][w1][t0][t1])%mo;
                        p[t1]=(p[t1]+t[m][w0][w1][t0][t1])%mo;
                    }
        fo(t0,0,mi[len]-1)
            fo(t1,0,mi[len]-1)
            if ((t1&t0)>0)w[t0]=(w[t0]+p[t1])%mo;
            else
                fo(_t0,0,mi[len]-1)
                v[t0][_t0]=(v[t0][_t0]+g[t1][_t0])%mo;
        f[0][0]=1;
        ans=0;
        fo(i,0,n-1)
            fo(t0,0,mi[len]-1)
            if (f[i][t0]){
                ans=(ans+LL(f[i][t0])*w[t0]%mo*v3[(n-i-1)*m]%mo)%mo;
                fo(_t0,0,mi[len]-1)
                    f[i+1][_t0]=(f[i+1][_t0]+LL(f[i][t0])*v[t0][_t0]%mo)%mo;
            }
        printf("%lld\n",ans);
    }
}

int main(){
    n=get();m=get();c=get();q=get();
    v3[0]=1;
    fo(i,1,n*m)v3[i]=v3[i-1]*3%mo;
    mi[0]=1;
    fo(i,1,12)mi[i]=mi[i-1]*2;
    len=m-c+1;
    if (n==2)work0();
    else work1();
    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值