P3290 [SCOI2016] 围棋

题意

定义一个棋盘为一个 \(n\times m\) 的网格,每个格子内有 'W','H','X' 三种字母之一。定义一个模板为一个 \(2\times c\) 的网格,每个格子内有 'W','H','X' 三种字母之一。当一个棋盘内包含模板时,称祂为好的棋盘。对于给出的 \(q\) 种模板,分别求出好的棋盘的总数。
\(n\le100,m\le12,c\le6,q\le5\)

思路

显然,需要 \(DP\),考虑计算不包含模板的方案数。
先对模板第一行和第二行分别建 \(ACAM\)
设当前在转移点 \((x,y)\),用一个二进制数记录轮廓线上的状态。\(1\) 表示这个位置和祂向前 \(c\) 个位置能和模板的第一行匹配,\(0\) 表示不能。设 \(f_{x,y,s,w1,w2}\) 表示二进制状态为 \(s\),当前行前面填的字符和模板第一行匹配到 \(w1\) 位,和模板第二行匹配到 \(w2\) 位的方案数。
枚举当前位置填什么字符并进行更新。时间复杂度 \(\mathcal O(qnm2^mc^2)\),实现的不好可能 \(TLE\)
发现二进制状态的前 \(c-1\) 位一定全是 \(0\),没有意义,可以删掉,复杂度优化为 \(\mathcal O(qnm2^{m-c+1}c^2)\)

代码

// Problem: P3290 [SCOI2016] 围棋
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3290
// Memory Limit: 512 MB
// Time Limit: 6000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
template<int mod>struct Modint{
    int z;
    Modint(){z=0;}
    Modint(int x){x%=mod;z=x<0?x+mod:x;}
    Modint(long long x){x%=mod;z=x<0?x+mod:x;}
    Modint(short x){x%=mod;z=x<0?x+mod:x;}
    Modint(char x){x%=mod;z=x<0?x+mod:x;}
    Modint(bool x){x%=mod;z=x<0?x+mod:x;}
    friend Modint operator+(Modint t,Modint t2){Modint ans;ans.z=(t.z+t2.z)%mod;return ans;}
    friend Modint operator*(Modint t,Modint t2){Modint ans;ans.z=1ll*t.z*t2.z%mod;return ans;}
    friend Modint operator-(Modint t,Modint t2){Modint ans;ans.z=(t.z-t2.z)%mod;return ans;}
    Modint operator-()const{return (Modint){-z};}
    Modint operator<<(const int t)const{Modint ans;ans.z=(z<<t)%mod;return ans;}
    Modint operator>>(const int t)const{Modint ans;ans.z=(z>>t)%mod;return ans;}
    Modint&operator+=(const Modint t){z=(z+t.z)%mod;return *this;}
    Modint&operator*=(const Modint t){z=1ll*z*t.z%mod;return *this;}
    Modint&operator-=(const Modint t){z=(z-t.z)%mod;return *this;}
    Modint&operator<<=(const int t){z=(z<<t)%mod;return *this;}
    Modint&operator>>=(const int t){z=(z>>t)%mod;return *this;}
    Modint&operator++(){z++,z%=mod;return *this;}
    Modint&operator--(){z--,z%=mod;return *this;}
    Modint operator++(int){Modint ls=*this;z++,z%=mod;return ls;}
    Modint operator--(int){Modint ls=*this;z--,z%=mod;return ls;}
    friend Modint ksm(Modint a,int b){
        Modint ans=1;
        while(b){if(b&1) ans=ans*a;a=a*a,b>>=1;}
        return ans;
    }
    friend void read(Modint&z){
        int x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=(x*10ll+c-'0')%mod,c=getchar();
        f?x=-x:0;
        z.z=x;
    }
    friend void write(Modint x){x.z<0?x.z+=mod:0;write(x.z);}
};
const int mod=1000000007;
#define M Modint<mod>
const int maxn=110,maxc=8;
int n,m,c,q;
M f[2][5000][maxc][maxc];
class ACAM{
private:
    struct node{int ch[5],fail,end;}t[maxn];
    int cnt;
public:
    void clear(){cnt=0;memset(t,0,sizeof(t));}
    void insert(vector<int>vt){
        int u=0;
        for(int i:vt){
            if(t[u].ch[i]==0) t[u].ch[i]=++cnt;
            u=t[u].ch[i];
        }
        t[u].end=1;
    }
    void build(){
        queue<int>q;
        for(int i=0;i<3;i++) if(t[0].ch[i]) q.push(t[0].ch[i]);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=0;i<3;i++){
                if(t[u].ch[i]) t[t[u].ch[i]].fail=t[t[u].fail].ch[i],q.push(t[u].ch[i]),t[t[u].ch[i]].end|=t[t[t[u].ch[i]].fail].end;
                else t[u].ch[i]=t[t[u].fail].ch[i];
            }
        }
    }
    bool is_end(int u){return t[u].end;}
    int nxt(int u,int x){return t[u].ch[x];}
}ac1,ac2;
int get(char x){
    if(x=='W') return 0;
    if(x=='B') return 1;
    return 2;
}
void solve(){
    memset(f,0,sizeof(f));
    ac1.clear(),ac2.clear();
    string s,t;read(s,t);
    vector<int>vt,vtt;
    for(int i=0;i<c;i++) vt.push_back(get(s[i])),vtt.push_back(get(t[i]));
    ac1.insert(vt),ac2.insert(vtt);
    ac1.build(),ac2.build();
    M ans=0;
    int nw=1,lt=0;
    f[nw][0][0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            swap(nw,lt);
            for(int s=0;s<(1<<m-c+1);s++) for(int w1=0;w1<=c;w1++) for(int w2=0;w2<=c;w2++) f[nw][s][w1][w2]=0;
            for(int s=0;s<(1<<m-c+1);s++) for(int w1=0;w1<=c;w1++) for(int w2=0;w2<=c;w2++){
                for(int ch=0;ch<3;ch++){
                    int ww1=ac1.nxt(w1,ch),ww2=ac2.nxt(w2,ch),ss=s;
                    if(j-c>=0){
                        if((s&(1<<j-1-c+1))&&ac2.is_end(ww2)) continue;
                        if(ac1.is_end(ww1)) ss|=(1<<j-1-c+1);
                        else if((s&(1<<j-1-c+1))) ss^=(1<<j-1-c+1);
                    }
                    f[nw][ss][ww1][ww2]+=f[lt][s][w1][w2];
                }
            }
        }
        for(int s=0;s<(1<<m-c+1);s++) for(int w1=0;w1<=c;w1++) for(int w2=0;w2<=c;w2++) if(w1!=0||w2!=0) f[nw][s][0][0]+=f[nw][s][w1][w2],f[nw][s][w1][w2]=0;
    }
    for(int s=0;s<(1<<m-c+1);s++) for(int w1=0;w1<=c;w1++) for(int w2=0;w2<=c;w2++) ans+=f[nw][s][w1][w2];
    write(ksm(M(3),n*m)-ans);
}
signed main(){
    read(n,m,c,q);
    while(q--) solve(),write("\n");
    return 0;
}
posted @ 2026-08-10 19:10  Link-Cut_Trees  阅读(7)  评论(0)    收藏  举报