洛谷P4831 Scarlet loves WenHuaKe

題面

題意

給出一個mnm*n的棋盤,問在其中放2m2*m個炮,使他們兩兩不會攻擊的方案數是多少。

做法

當n,m都小於等於2000時,直接記錄有幾列放了一個棋子,幾列沒放棋子,即可逐行轉移。
當大於2000時,可以利用nm<=10n-m<=10的性質,考慮最後一列放幾個棋子,分以下三種方案進行討論:
1.最後一列放兩個棋子,考慮這兩個棋子所在的兩行的另外兩顆棋子a,b:
如果a,b在同一列,則f(m,n)+=f(m2,n2)(m(m1)/2)(n1)f(m,n)+=f(m-2,n-2)*(m*(m-1)/2)*(n-1)
反之,則可以合併這兩行f(m,n)+=f(m1,n1)(m(m1)/2)2f(m,n)+=f(m-1,n-1)*(m*(m-1)/2)*2
2.最後一列只放一個棋子,則這個棋子所在行的另一個棋子的所在列最多放1個棋子,因此只要減去那一列放兩個棋子的方案數即可直接轉移。
3.最後一列不放棋子,則f(m,n)+=f(m,n1)f(m,n)+=f(m,n-1)

代碼

#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);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章