bzoj4000 [TJOI2015]棋盘(状压dp+矩阵快速幂)

题目中的标号都是从0开始的!所以攻击模板中你的位置为中间那一行!所以可以只考虑相邻两行的转移!

先处理出f[s1][s2]表示相邻两行状态为s1,s2是否可行。
然后n很大,所以我们要矩阵加速。
复杂度 O((2m)3logn)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 70
#define uint unsigned int
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(S==T) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,nn,m,bin[10];
vector<int>mp[3];
struct Matrix{
    uint a[N][N];
    Matrix(){}
    Matrix(bool t){memset(a,0,sizeof(a));if(t) for(int i=0;i<=n;++i) a[i][i]=1;}
    friend Matrix operator*(Matrix a,Matrix b){
        Matrix res(0);
        for(int i=0;i<=n;++i)
            for(int j=0;j<=n;++j)
                for(int k=0;k<=n;++k)
                    res.a[i][j]+=a.a[i][k]*b.a[k][j];return res;
    }friend Matrix operator^(Matrix a,int k){
        Matrix res(1);
        for(;k;k>>=1,a=a*a) if(k&1) res=res*a;return res;
    }
}trans,a;
inline bool jud(int s1,int s2){
    for(int i=0;i<m;++i){
        if(!(bin[i]&s1)) continue;
        for(int j=0;j<mp[1].size();++j){
            int x=mp[1][j]+i;if(x<0||x>=m) continue;
            if(bin[x]&s1) return 0;
        }for(int j=0;j<mp[2].size();++j){
            int x=mp[2][j]+i;if(x<0||x>=m) continue;
            if(bin[x]&s2) return 0;
        }
    }for(int i=0;i<m;++i){
        if(!(bin[i]&s2)) continue;
        for(int j=0;j<mp[1].size();++j){
            int x=mp[1][j]+i;if(x<0||x>=m) continue;
            if(bin[x]&s2) return 0;
        }for(int j=0;j<mp[0].size();++j){
            int x=mp[0][j]+i;if(x<0||x>=m) continue;
            if(bin[x]&s1) return 0;
        }
    }return 1;
}
int main(){
//  freopen("a.in","r",stdin);
    nn=read();m=read();int p=read(),k=read();bin[0]=1;
    for(int i=1;i<=m;++i) bin[i]=bin[i-1]<<1;n=bin[m]-1;
    for(int i=0;i<3;++i){
        for(int j=0;j<p;++j){
            int x=read();if(i==1&&j==k) continue;
            if(x) mp[i].push_back(j-k);
        }
    }for(int s1=0;s1<=n;++s1)
        for(int s2=0;s2<=n;++s2) trans.a[s1][s2]=jud(s1,s2);
    a.a[0][0]=1;a=a*(trans^nn);uint ans=0;
    for(int i=0;i<=n;++i) ans+=a.a[0][i];
    printf("%u\n",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值