ybtoj 背包问题 J. 5.计数问题
好题。(但一本通写的好抽象 细节也没讲出来
首先 这是期望题
显然不是期望题 \((2m)^n\) 显然是走 \(n\) 步的总方案数 那么实际求得就是所有方案经过的不同位置的数量
看起来还是不可求
我们考虑枚举每一步 \(i\quad\) 有几种方案会走到新的一块 对答案产生贡献 设为 \(dp_i\) 最后累加即可
\(n\le 2000\) 这东西我们可以 \(n^2\) dp 考虑容斥
所求答案实际等于 走 \(i\) 步的总方案数 \(-\) 第 \(i\) 步走在走过的块的方案数
总方案数显然 为 \((2m)^i\) 考虑后面那个东西咋求
枚举之前走过的每一步 第 \(i\) 步走在走过的块的方案数就是 \(\sum dp_j\times back_{(i-j)/2,k}\)
设 \(back_{i,k}\) 为 \(k\) 维下 走 \(2i\) 步走回原来的点的方案数
上面的 \(j\) 从 \(i\) 往回枚举 每次减2
因为 \(i-j\) 为奇数的点 走 \(i\) 步必然不会回到那个位置
以 \(i-1\) 为例 \(\quad i-1\) 的下一步只有可能是 \(i\) 和 \(i-2\quad\) 不可能第 \(i\) 步停在 \(i-1\) 的位置
\(i-3\quad i-5...\) 同理
最后只剩一个问题 \(back_{i,k}\)咋求
显然在一维时 \(back_{i,1}=C_{2i}^i\) (相当于在一共的 \(2i\) 步中 选出 \(i\) 步往回走)
从低维推出高维 \(back_{i,k}=\sum back_{p,k-1}\times back_{i-p,1}\times C_{2i}^{2p}\)
解释一下 \(C_{2i}^{2p}\) 类似背包思想(就这里用背包了/fn) 在 \(2i\) 步中选 \(2p\) 步给前 \(k-1\) 维 用乘法原理乘起来
该步复杂度 \(O(n^2m)\)
最后 \(dp_i=(2m)^i-\sum dp_j\times back_{(i-j)/2,k}\)
该步复杂度 \(O(n^2)\)
\(ans=\sum dp_i\times (2m)^{n-i}\) (第 \(i\) 步后剩下 \(n-i\) 步 方案数为 \((2m)^{n-i}\) )
这样就做完了。。。复杂度还是 \(O(n^2m)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline
#define int ll
#define gc getchar
#define pc putchar
const int N=2005;
const int M=15;
const int inf=0x7fffffff;
const double eps=1e-6;
inl int read(){
int x=0,f=1;char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return x*f;
}
inl void write(int x){
if(x<0){pc('-');x=-x;}
if(x>9)write(x/10);
pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,mod,back[N][M],f[N],C[N][N],ans;
inl int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
inl void init(){
for(int i=0;i<=2e3+2;i++)C[i][0]=1;
for(int i=1;i<=2e3+2;i++)
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
signed main(){
n=read();m=read();mod=read();
init();
for(int i=0;i<=n/2+1;i++)back[i][1]=C[2*i][i];
for(int k=2;k<=m;k++)
for(int i=0;i<=n/2+1;i++)
for(int p=0;p<=i;p++)
back[i][k]=(back[i][k]+back[p][k-1]*back[i-p][1]%mod*C[2*i][2*p]%mod)%mod;
for(int i=0;i<=n;i++){
f[i]=qpow(2*m,i);
for(int j=i-2;j>=0;j-=2)
f[i]=(f[i]-f[j]*back[(i-j)/2][m]+mod)%mod;
}
for(int i=0;i<=n;i++)ans=(ans+f[i]*qpow(2*m,n-i))%mod;
if(ans<0)ans+=mod;
writel(ans);
return 0;
}