1. 程式人生 > 其它 >【20211027 模擬賽】慶典

【20211027 模擬賽】慶典

Statement

【題目描述】 戰狂在昌和帝國的首都法法城召開了慶典,向一萬名最傑出的士 兵分發了用魔法豬做的豬肉餃子,士兵們吃了豬肉餃子後,戰鬥力大 幅提高。 為了保護戰狂的安全以及維護現場秩序,大預言家抽調了 n 名普 通士兵組成了 m 個小隊完成一些不同的任務。由於一些特殊的原因, 所有小隊的人數都互不相同。 你需要求出有多少種可能的組隊方案。注意士兵是相同的,而小 隊是不同的。

【輸入資料】 第一行兩個個整數 n,m。

【輸出資料】 一行一個數表示答案。對 998244353 取模。

【樣例輸入】 16 4 【樣例輸出】 216

【資料範圍】 對於 20%的資料,n,m<=20。 對於 50%的資料,n,m<=3000。 對於 100%的資料,n,m<=100000

Solution

題目即是問把 \(n\) 個相同的球放到 \(m\) 個不同的箱子裡面,要求每個箱子裡面球的數量不同,方案數

為了滿足球數不同的要求,我們考慮先給第 \(i\) 個箱子放 \(i\) 個球(很妙的手段)

假設剩下了 \(n^{\prime}\) 個球,接下來的操作手段顯然是說每次可以把一個字尾整體增加 \(1\)

這其實成為了一個類似完全揹包的問題,總容量 \(n^{\prime}\) ,物品體積 \(1\dots n^{\prime}\) ,數量無限。

不要忘了箱子不同,答案要乘一個 \(m!\)


還有另外一個想法是說,這其實是一個 整數劃分問題

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
const int mod = 998244353;

void file(){
    freopen("celebration.in","r",stdin);
    freopen("celebration.out","w",stdout);
}
int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s*w;
}

int f[N];
int n,m;

signed main(){
    n=read(),m=read();
    if(2*n<m*(m+1))puts("0");
    else{
        n-=m*(m+1)/2;
        f[0]=1;
        for(int i=1;i<=m;++i)
            for(int j=i;j<=n;++j)
                (f[j]+=f[j-i])%=mod;
        for(int i=1;i<=m;++i)(f[n]*=i)%=mod;
        printf("%lld\n",f[n]);
    }
    return 0;
}