ybtoj 背包问题 J. 5.计数问题

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;
}
posted @ 2023-10-13 15:12  xiang_xiang  阅读(36)  评论(0编辑  收藏  举报