DP的设计最重要的是找到合理的方案,如果求的是方案,那就要要求不重不漏。
一般这种都跟组合数结合,一定要给组合数公式想到一个实际的物理意义,这样理解起来比较方便
这题我确实不会,但是看题解觉得很牛逼,也很有道理,关键就是找到一个点能使得所有状态不重不漏
一般都是考虑题目给的信息,很多情况下都是考虑集合中有什么数,然后以他为立足点,这是我的一些小思考
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<ll,ll> pll; const int N=2e5+10; const int M=2e6+10; const int inf=0x3f3f3f3f; const ll mod=998244353; int f[5010][5010]; int n; int tmp[N],g[N]; int cnt[N],sum[N]; ll qmi(ll a,ll b){ ll res=1; while(b){ if(b&1){ res=res*a%mod; } a=a*a%mod; b>>=1; } return res; } int C(int a,int b){ if(a<b) return 0; return 1LL*tmp[a]*g[b]%mod*g[a-b]%mod; } void init(){ int i,j; tmp[0]=1; for(int i=1;i<5000;++i) tmp[i]=1LL*tmp[i-1]*i%mod; g[4999]=qmi(tmp[4999],mod-2); for(int i=4998;i>=0;--i) g[i]=1LL*g[i+1]*(i+1)%mod; } int main(){ ios::sync_with_stdio(false); int t; cin>>t; init(); while(t--){ int n,k; cin>>n>>k; int r=n%(k+1); int i,j; for(i=0;i<=n;i++){ for(j=0;j<=n;j++){ f[i][j]=0; } } f[0][0]=1; if(r==0){ for(i=1;i<n;i++){ cout<<0<<" "; } cout<<0; cout<<endl; } else if(n<=k){ for(i=1;i<n;i++){ cout<<1<<" "; } cout<<1; cout<<endl; } else{ for(i=0;i<n;i++){ for(j=0;j<=n;j++){ if(j+k+i-1<=n-r) f[i+1][j+k]=(f[i+1][j+k]+f[i][j])%mod;//i被当成最小数删除 if(j) f[i+1][j-1]=(f[i+1][j-1]+1ll*f[i][j]*j%mod)%mod;//被当作随机数删除,有j个位置可以选 } } ll tot=0; for(i=1;i<=n;i++){ int j=n-r-i+1;//还要选的数目 sum[i]=1ll*f[i-1][j]*C(n-i,j)%mod*tmp[j]%mod;//组合数+排列 tot=(tot+sum[i])%mod;//记录总方案数 cnt[i]=1ll*f[i-1][j]*C(n-i-1,j)%mod*tmp[j]%mod;//当x>i时 x存留,i提供的贡献 } tot=qmi(tot,mod-2); ll res=0; for(i=1;i<=n;i++){ if(i!=n) cout<<(sum[i]+res)*tot%mod<<" "; else cout<<(sum[i]+res)*tot%mod<<endl; res=(res+cnt[i])%mod; } } } return 0; }