P8386 [PA2021] Od deski do deski
一道很抽象的 \(\text {dp}\)?
状态就比较抽象。注意到 \(m\) 有 \(10^9\),肯定不能带到状态里。但是我们可以注意到:如果当前序列 \(S\) 已经合法,且有 \(S+x\) 合法,那么 \(S+x+x\) 也一定合法,因为我们可以把两个 \(x\) 消掉。因此,可以设计 \(f_{i,j}\) 表示长度为 \(i\) 的序列,往后放 \(j\) 种数依然合法的方案数。同时还要考虑当前序列是否合法,于是增加一维 \(0/1\) 表示当前序列是否合法。
然后考虑转移。首先如果当前为 \(f_{i,j,1}\),那么长为 \(i+1\) 的序列再往后放这 \(j\) 种数依然合法,因为可以互相消掉,且种类不会增多。当前这一位可以有 \(j\) 种放法,所以 \(f_{i+1,j,1} \gets f_{i,j,1} \times j\)。同时如果当前 \(f_{i,j,0}\) 是不合法的,那就是说单出来一个数不能配对,因此我们考虑在 \(i+1\) 填 \(j\) 种数中的一个,使它能与之前的配对并消除,变成合法.因此 \(f_{i+1,j,1} \gets f_{i,j,0} \times j\)。
再来看不合法的情况。如果当前 \(f_{i,j,1}\),我们在后面填了另外的 \(m-j\) 种数,此时这个数就会单出来无法与前面的匹配消掉,并会让种数加一,\(f_{i+1,j+1,0} \gets f_{i,j,1} \times (m-j)\)。同理,如果当前不行,那再加那 \(m-j\) 种数中的一个依然不合法。所以 \(f_{i+1,j,0} \gets f_{i,j,0} \times (m-j)\)。
答案就是 \(\sum_{i=1}^{n} f_{n,i,1}\)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
ll m,dp[3005][3005][2],ans;
const int mod=1e9+7;
int main(){
cin>>n>>m;
dp[0][0][1]=1;
for(int i=0;i<n;++i){
for(ll j=0;j<=i;++j){
dp[i+1][j][1]=(dp[i+1][j][1]+dp[i][j][1]*j%mod)%mod;
dp[i+1][j+1][0]=(dp[i+1][j+1][0]+dp[i][j][1]*(m-j)%mod)%mod;
dp[i+1][j][0]=(dp[i+1][j][0]+dp[i][j][0]*(m-j)%mod)%mod;
dp[i+1][j][1]=(dp[i+1][j][1]+dp[i][j][0]*j%mod)%mod;
}
}
for(int i=1;i<=n;++i)ans=(ans+dp[n][i][1])%mod;
cout<<ans;
return 0;
}