題面
題意
給出一個的棋盤,問在其中放個炮,使他們兩兩不會攻擊的方案數是多少。
做法
當n,m都小於等於2000時,直接記錄有幾列放了一個棋子,幾列沒放棋子,即可逐行轉移。
當大於2000時,可以利用的性質,考慮最後一列放幾個棋子,分以下三種方案進行討論:
1.最後一列放兩個棋子,考慮這兩個棋子所在的兩行的另外兩顆棋子a,b:
如果a,b在同一列,則
反之,則可以合併這兩行
2.最後一列只放一個棋子,則這個棋子所在行的另一個棋子的所在列最多放1個棋子,因此只要減去那一列放兩個棋子的方案數即可直接轉移。
3.最後一列不放棋子,則
代碼
#include<bits/stdc++.h>
#define ll long long
#define P pair<ll,ll>
#define mp make_pair
#define fi first
#define se second
#define N 100100
#define M 998244353
using namespace std;
ll m,n,dp[N][15];
inline ll C2(ll u){return u*(u-1)/2%M;}
ll dfs(ll u,ll v);
inline ll calc(ll u,ll v){return C2(u)*((2*dfs(u-1,v)+(u+v-1)*dfs(u-2,v)%M)%M)%M;}
ll dfs(ll u,ll v)
{
if(!u&&!v) return 1;
if(u<0 || v<0) return 0;
if(dp[u][v]!=-1) return dp[u][v];
ll res=0;
res+=calc(u,v);
if(v)
{
res+=u*(u+v-1)%M*((dfs(u-1,v)+M-calc(u-1,v))%M)%M;
res+=dfs(u,v-1);
}
return dp[u][v]=res%M;
}
namespace solve
{
ll jl[2010][2010];
ll dfs(ll u,ll v)
{
if(u*2+v+m*2==n*2) return 1;
if(jl[u][v]!=-1) return jl[u][v];
ll res=0;
if(u>1) res+=dfs(u-2,v+2)*u*(u-1)/2%M;
if(u&&v) res+=dfs(u-1,v)*u*v%M;
if(v>1) res+=dfs(u,v-2)*v*(v-1)/2%M;
return jl[u][v]=res%M;
}
void work()
{
memset(jl,-1,sizeof(jl));
cout<<dfs(n,0);
}
}
int main()
{
memset(dp,-1,sizeof(dp));
ll i,j;
cin>>m>>n;
if(m<=2000&&n<=2000)
{
solve::work();
return 0;
}
cout<<dfs(m,n-m);
}