atcoder ARC104 D Multiset Mean

ARC104 D

传送门

题意:给你三个数字: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;
}

posted @ 2020-10-06 15:53  John_Ran  阅读(302)  评论(0编辑  收藏  举报