【刷题】BZOJ 4000 [TJOI2015]棋盘

Description

222(1).png

Input

输入数据的第一行为两个整数N,M表示棋盘大小。第二行为两个整数P,K,

表示攻击范围模板的大小,以及棋子在模板中的位置。接下来三行,

每行P个数,表示攻击范围的模版。每个数字后面一个空格。

Output

一个整数,表示可行方案Mod 2 ^32

Sample Input

2 2
3 1
0 1 0
1 1 1
0 1 0

Sample Output

7

HINT

1<=N<=10^6,1<=M<=6

Solution

这题有点无聊
先考虑把按照棋盘每一行去状压dp,然后发现跑不了
于是又了解到每次状压的转移方程是一样的,并且 \(m\) 小得可怜,于是就可以用矩阵乘法优化
预处理的话就直接暴力枚举每一个状态和下一个状态,看能否转移就好了

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=1000000+10,MAXM=(1<<6)+10;
int n,m,P,K,G[4],atk1,atk2;
ui ans;
struct Matrix{
    ui a[MAXM][MAXM];
    inline Matrix operator * (const Matrix &A) const {
        Matrix B;
        for(register int i=0;i<(1<<m);++i)
            for(register int j=0;j<(1<<m);++j)
            {
                B.a[i][j]=0;
                for(register int k=0;k<(1<<m);++k)B.a[i][j]+=a[i][k]*A.a[k][j];
            }
        return B;
    };
};
Matrix A;
template<typename T> inline void read(T &x)
{
    T data=0,w=1;
    char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline int check(int st,int ed)
{
    atk1=0,atk2=0;
    for(register int i=0;i<m;++i)
        if(st&(1<<i))
        {
            if(P-K-1>=i)atk1|=(G[1]>>P-K-1-i),atk2|=(G[2]>>P-K-1-i);
            else atk1|=(G[1]<<i-P+K+1),atk2|=(G[2]<<i-P+K+1);
        }
    if((atk1&st)||(atk2&ed))return 0;
    atk1=0;atk2=0;
    for(register int i=0;i<m;++i)
        if(ed&(1<<i))
        {
            if(P-K-1>=i)atk1|=(G[1]>>P-K-1-i),atk2|=(G[0]>>P-K-1-i);
            else atk1|=(G[1]<<i-P+K+1),atk2|=(G[0]<<i-P+K+1);
        }
    if((atk1&ed)||(atk2&st))return 0;
    return 1;
}
inline Matrix qexp(Matrix a,int b)
{
    Matrix res=a;b--;
    while(b)
    {
        if(b&1)res=res*a;
        a=a*a;
        b>>=1;
    }
    return res;
}
int main()
{
    read(n);read(m);read(P);read(K);
    for(register int i=0;i<3;++i)
        for(register int j=0,x;j<P;++j)read(x),G[i]|=(x<<P-j-1);
    G[1]^=(1<<P-K-1);
    for(register int i=0;i<(1<<m);++i)
        for(register int j=0;j<(1<<m);++j)A.a[i][j]=check(i,j);
    A=qexp(A,n);
    for(register int i=0;i<(1<<m);++i)ans+=A.a[0][i];
    printf("%u\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/hongyj/p/9544216.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值