题意:给你三个数字:N,K,M(N,K<=100)。对于[1,N]内的每一个数x,你需要解决以下问题:对于[1,N]内的每一个数字,你最多取K个,这样组成的数组的平均值是x。
首先我们需要一个转化:对于其中的一个问题,将[1,N]中的数字写成 -x,-(x-1),-(x-2),... , -1, 0, 1, 2, ... ,y-1, y。现在将大于0和小于0的数字分成两个部分。在这两个部分中,每一个数字最多选择K次。如果使得它们的和一样,那就跟原问题是等价的。这给我们一个相当naive的dp:
[dp[i][j]表示的是,考虑[1, i]中的数字,每一个最多选择K个,和为j的方案数。转移的时候,要用一个前缀和去维护,这使得转移更快。
]
最终答案是,相同的值的方案数*(K+1)-1。其中K+1的贡献其实是你要考虑x的个数,0-K都是可行的。-1的意义是,去掉空集。
如果你对每一个x,都去跑一遍这个dp,然后会发现复杂度是O(n5),直接gg。事实上,我们可以预处理出这个dp数组,在处理每一次询问的时候,直接调用这个数组的值就好了。最终复杂度O(n4),可以通过。
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int n, k, MOD;
void sub(int& x, int y){
x-=y;
if(x<0)x+=MOD;
}
void add(int& x, int y){
x+=y;
if(x>MOD)x-=MOD;
}
int dp[N][N*N*N];
void solve(){
dp[0][0]=1;
for(int i=1;i<=n;++i){
int up=k*(i+1)*i/2;
for(int j=0;j<=up;++j){
dp[i][j]=dp[i-1][j];
if(j>=i){
add(dp[i][j],dp[i][j-i]);
}
}
for(int j=up;j>=(k+1)*i;--j){
sub(dp[i][j],dp[i][j-(k+1)*i]);
}
}
}
void getAns(){
for(int i=1;i<=n;++i){
int ans=0;
int l=i-1, r=n-i;
int up=min(k*l*(l+1)/2,k*r*(r+1)/2);
for(int j=0;j<=up;++j){
add(ans,1ll*dp[l][j]*dp[r][j]%MOD);
}
ans=1ll*ans*(k+1)%MOD;
add(ans,-1);
cout<<ans<<endl;
}
}
int main(){
cin>>n>>k>>MOD;
solve();
getAns();
return 0;
}