决战

题目大意

给你一个3*n网格图。
你有m个士兵,每个士兵会对3*3的范围内进行攻击,攻击矩阵初始给定。
有多少种放置士兵的方法,使得士兵互不攻击。

DP

很显然能dp
这个dp还很显然能NTT优化
但是常数大显然不一定跑得过暴力
于是我们就暴力吧(雾

#pragma GCC optimize(2)
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int mo=998244353,maxn=2500+10;
int f[2][maxn*3][10];
int a[10],one[10],dis[10][10],d[5][5];
int cc[10][10],len[10];
bool c[5][5],ha[5][5],bz[10],czy,gjx,wzd;
int i,j,k,l,r,t,x,y,n,m,top,ans,now,down,up;
int qsm(int x,int y){
    if (!y) return 1;
    int t=qsm(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
void work(){
    ans=1;
    fo(i,1,3*n) ans=(ll)ans*i%mo;
    fo(i,1,m) ans=(ll)ans*qsm(i,mo-2)%mo;
    fo(i,1,3*n-m) ans=(ll)ans*qsm(i,mo-2)%mo;
    printf("%d\n",ans);
}
int main(){
    one[1]=one[2]=one[4]=1;
    one[3]=one[5]=one[6]=2;
    one[7]=3;
    scanf("%d%d",&n,&m);
    gjx=1;
    fo(i,0,2)
        fo(j,0,2){
            scanf("%d",&c[i][j]);
            if (c[i][j]&&(i!=1||j!=1)) gjx=0;
        }
    ha[2][0]=c[0][0];
    ha[2][2]=c[2][0];
    ha[0][2]=c[2][2];
    ha[0][0]=c[0][2];
    ha[1][0]=c[0][1];
    ha[2][1]=c[1][0];
    ha[1][2]=c[2][1];
    ha[0][1]=c[1][2];
    ha[1][1]=c[1][1];
    fo(i,0,2)
        fo(j,0,2)
            c[i][j]=ha[i][j];
    if (gjx){
        work();
        return 0;
    }
    fo(i,0,7){
        if (!c[0][1]&&!c[2][1]){
            wzd=1;
            a[++top]=i;
            continue;
        }
        if ((i&1)&&(i&2)) continue;
        if ((i&2)&&(i&4)) continue;
        a[++top]=i;
    }
    fo(i,1,top) bz[a[i]]=1;
    fo(i,0,7)
        fo(j,0,7){
            if (!bz[i]||!bz[j]) continue;
            fo(k,0,2)
                if ((i&(1<<k))) d[k][0]=1;else d[k][0]=0;
            fo(k,0,2)
                if ((j&(1<<k))) d[k][1]=1;else d[k][1]=0;
            czy=1;
            fo(k,0,2)
                fo(l,0,1)
                    if (d[k][l])
                        fo(x,-1,1)
                            fo(y,-1,1)
                                if ((x||y)&&k+x>=0&&k+x<=2&&l+y>=0&&l+y<=1&&c[x+1][y+1]&&d[k+x][l+y]) czy=0;
            if (czy) dis[i][j]=1;
        }
    fo(i,1,top){
        fo(j,1,top)
            if (dis[a[i]][a[j]]) cc[a[i]][++len[a[i]]]=a[j];
    }
    now=0;
    fo(i,1,top) f[now][one[a[i]]][a[i]]=1;
    fo(i,1,n-1){
        down=max(m-3*(n-i),0);
        if (wzd) up=min(3*i,m);else up=min(2*i,m);
        //down=0;
        fo(k,1,top)
            fo(j,down,up)
                f[1-now][j][a[k]]=0;
        /*fo(j,0,m)
            fo(k,1,top)
                if (f[now][j][a[k]])
                    fo(l,1,top)
                        if (dis[a[k]][a[l]]){
                            f[1-now][j+one[a[l]]][a[l]]+=f[now][j][a[k]];
                            if (f[1-now][j+one[a[l]]][a[l]]>=mo) f[1-now][j+one[a[l]]][a[l]]-=mo;
                        }*/
        fo(j,down,up)
            fo(k,1,top){
                x=a[k];
                if (f[now][j][x])
                    fo(l,1,len[x]){
                        r=cc[x][l];
                        //if (dis[x][r]){
                        y=j+one[r];
                            f[1-now][y][r]+=f[now][j][x];
                            if (f[1-now][y][r]>=mo) f[1-now][y][r]-=mo;
                    //  }
                    }
            }
        now=1-now;
    }
    fo(i,1,top){
        ans+=f[now][m][a[i]];
        if (ans>=mo) ans-=mo;
    }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值