玲瓏oj 1032A-B(組合數學)

Time Limit:1s Memory Limit:128MByte

Submissions:528Solved:105

DESCRIPTION
你有n個球,需要把他們放到m個盒子裏。要求擁有最多球的盒子唯一,問方案數。
INPUT
一行兩個數n、m(n、m≤500)
OUTPUT
一行一個數,表示方案數。答案對998244353取模。
SAMPLE INPUT
5 2
SAMPLE OUTPUT
6


思路:

測試數據很水,很暴力的dp也能過,O(n^3)

dp[i][j]表示i個盒子裏面一共放了j個球的情況。

假設球數最多的盒子裏面放了k個球,那麼剩下的m-1個盒子裏面只能放n-k個球,每個盒子最多[0,k-1]個球。

dp[i][j] = ∑dp[i-1][j-x], x∈[0, k]。

用pre[i][j]來求dp[i][j]的前綴和來優化一下。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
#define ll long long
const ll mod = 998244353;
ll dp[550][550];
ll pre[550][550];

int main(){
	int n, m;
	scanf("%d%d", &n, &m);
	dp[0][0] = 1;
	for(int i=0; i<=n; i++) pre[0][i]=1;
	ll ans=0;
	for(int k=0; k<=n; k++){
		for(int i=1; i<m; i++){
			for(int j=0; j<=n; j++){
				dp[i][j]=0;
				/*for(int x=0; x<k; x++){
					ll sum = j-x>=0 ? dp[i-1][j-x]:0;
					dp[i][j] = (dp[i][j]+sum)%mod;
				}*/
				dp[i][j] = (pre[i-1][j] - (j-k>=0?pre[i-1][j-k]:0) + mod)%mod;
				pre[i][j] = ((j==0?0:pre[i][j-1]) + dp[i][j])%mod;
			}
		}
		ans = (ans+m*dp[m-1][n-k])%mod;
	}	
	printf("%lld\n", ans);
	return 0;
} 

看了玲瓏oj上面的題解,知道了一種組合數學+容斥的解法

【ps:玲瓏oj上面的題解有點小錯誤,,這個式子裏減去的那部分忘記 *(-1)^k,整個式子要乘以盒子的數量,也就是m,非球的數量n】

利用容斥原理,最終的方案數=總方案數-不合法的方案數

find(i, j)表示將i個球放到j個盒子裏的方案數,盒子裏面允許爲空

find(i, j) = C[i+j-1][j-1] 【這個公式不懂的可以參照這個博客:codeforces397C

先枚舉第一個盒子的數量,假設這是球數量最多的一個盒子,記爲x,然後枚舉有k個盒子球的數量>=第一個盒子

因此 ans = m*( find(n, m) - ∑((-1)^k) * (C[m-1][k] * find(n-x*(k+1),m-1) ) ), x∈[0,n], k∈[1,m-1]

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
#define ll long long
const ll mod = 998244353;
ll C[1050][1050];

ll find(int x, int y){
	if(x+y-1<0 || y-1<0) return 0;
	return C[x+y-1][y-1];
}

int main(){
	int n, m;
	scanf("%d%d", &n, &m);
	
	C[0][0]=1;
	C[1][0] = C[1][1] = 1;  
    for (int i = 2; i <= 1000; i++){  
        C[i][0] = 1;  
        for (int j = 1; j <= 1000; j++)  
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;  
    }  
	
	ll ans = 0;
	for(int x=0; x<=n; x++){
		for(int k=1; k<m; k++){
			ll sum = find(n-x*(k+1), m-1);
			ans = (ans + (k%2==1?1:-1)*C[m-1][k]*sum%mod)%mod;
			ans = (ans+mod)%mod;
		}
	}
	
	ans = (find(n, m)-ans+mod)%mod;
	ans = m*ans%mod;
	printf("%lld\n", ans);
	return 0;
} 














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章