Ant Counting POJ - 3046
贝西有T种蚂蚁共A只,每种蚂蚁有Ni只,同种蚂蚁不能区分,不同种蚂蚁可以区分,记Sumi为i只蚂蚁构成不同的集合的方案数,问Sumk(S≤k≤B)之和。
Input
第一行4个整数T、A、S、B。 接下来A行,每行一个整数,代表这只蚂蚁的种类。
Output
输出Sumk(S≤k≤B)之和。 (数据范围见原文)
Examples
Sample Input
3 5 2 3
1
2
2
1
3
Sample Output
10
Hint
题意:
题解:
非常经典的多重集组合数问题, 即有N种物品, 第 i 种物品有 ai 个, 从中取出 m 个, 共有多少种取法 ?
d
p
[
i
]
[
j
]
:
前
i
种
物
品
取
j
个
的
组
合
数
dp[i][j]:前i种物品取j个的组合数
dp[i][j]:前i种物品取j个的组合数
根据加法计数原理, 为了从前 i 种物品中取出 j 个, 需要考虑前 i-1个物品中取出的所有可能. 即
d
p
[
i
]
[
j
]
=
∑
k
=
0
m
i
n
(
j
,
a
[
i
]
)
d
p
[
i
−
1
]
[
j
−
k
]
dp[i][j] = \sum_{k=0}^{min(j,a[i])}dp[i-1][j-k]
dp[i][j]=∑k=0min(j,a[i])dp[i−1][j−k]
直接计算这个递推关系的话复杂度为O(nm^2), 我们考虑进一步优化
∑
k
=
0
m
i
n
(
j
,
a
[
i
]
)
d
p
[
i
−
1
]
[
j
−
k
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
−
1
]
[
j
−
1
]
+
.
.
.
+
d
p
[
i
−
1
]
[
j
−
a
[
i
]
]
+
d
p
[
i
−
1
]
[
j
−
a
[
i
]
−
1
]
−
d
p
[
i
−
1
]
[
j
−
a
[
i
]
−
1
]
\sum_{k=0}^{min(j,a[i])}dp[i-1][j-k]=dp[i-1][j]+dp[i-1][j-1]+...+dp[i-1][j-a[i]]+dp[i-1][j-a[i]-1]-dp[i-1][j-a[i]-1]
∑k=0min(j,a[i])dp[i−1][j−k]=dp[i−1][j]+dp[i−1][j−1]+...+dp[i−1][j−a[i]]+dp[i−1][j−a[i]−1]−dp[i−1][j−a[i]−1] (a[i] <= j, 后两项为手动添加)
∑ k = 0 m i n ( j , a [ i ] ) d p [ i − 1 ] [ j − k ] = d p [ i − 1 ] [ j ] + ∑ k = 0 m i n ( j − 1 , a [ i ] ) − d p [ i − 1 ] [ j − a [ i ] − 1 ] \sum_{k=0}^{min(j,a[i])}dp[i-1][j-k]=dp[i-1][j]+\sum_{k=0}^{min(j-1, a[i])}-dp[i-1][j-a[i]-1] ∑k=0min(j,a[i])dp[i−1][j−k]=dp[i−1][j]+∑k=0min(j−1,a[i])−dp[i−1][j−a[i]−1]
∑ k = 0 m i n ( j , a [ i ] ) d p [ i − 1 ] [ j − k ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 − a [ i ] ] − d p [ i − 1 ] [ j − a [ i ] − 1 ] \sum_{k=0}^{min(j,a[i])}dp[i-1][j-k]=dp[i-1][j]+dp[i][j-1-a[i]]-dp[i-1][j-a[i]-1] ∑k=0min(j,a[i])dp[i−1][j−k]=dp[i−1][j]+dp[i][j−1−a[i]]−dp[i−1][j−a[i]−1]
当j < a[i]时更简单了, 可以消去后面项推出dp[i][j] = d[i-1][j] + d[i-1][j - a[i] - 1]
注意题目要求取后6位, 要%1e6, 同时要考虑负数, 需要(…+mod)%mod
经验小结:
看到这种与组合数学相关的问题, 大胆先去推公式, 找出前项和后项间的关系, 从而简化递推式
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const int inf = 1<<30;
const LL maxn = 1e5+10;
const int mod = 1e6;
int N, M, l, r, a[1010];
int dp[1010][maxn]; //前i种取j个的组合数
int main()
{
int input;
cin >> N >> M >> l >> r;
for(int i = 1; i <= M; ++i){
cin >> input;
++a[input];
}
//初始化, 一个也不取总有一种
for(int i = 0; i <= N; ++i)
dp[i][0] = 1;
for(int i = 1; i <= N; ++i){
for(int j = 1; j <= M; ++j){
if(j-a[i]-1 >= 0) //j>=a[i]时
dp[i][j] = (dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1-a[i]]+mod)%mod;
else //j<a[i]时
dp[i][j] = (dp[i][j-1]+dp[i-1][j]+mod)%mod;
}
}
int ans = 0;
for(int i = l; i <= r; ++i)
ans = (ans+dp[N][i]+mod)%mod;
cout << ans << endl;
return 0;
}