Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 6037 | Accepted: 2246 |
Description
This made for a large number of different sets of ants!
Being a bit mathematical, Bessie started wondering. Bessie noted that the hive has T (1 <= T <= 1,000) families of ants which she labeled 1..T (A ants altogether). Each family had some number Ni (1 <= Ni <= 100) of ants.
How many groups of sizes S, S+1, ..., B (1 <= S <= B <= A) can be formed?
While observing one group, the set of three ant families was seen as {1, 1, 2, 2, 3}, though rarely in that order. The possible sets of marching ants were:
3 sets with 1 ant: {1} {2} {3}
5 sets with 2 ants: {1,1} {1,2} {1,3} {2,2} {2,3}
5 sets with 3 ants: {1,1,2} {1,1,3} {1,2,2} {1,2,3} {2,2,3}
3 sets with 4 ants: {1,2,2,3} {1,1,2,2} {1,1,2,3}
1 set with 5 ants: {1,1,2,2,3}
Your job is to count the number of possible sets of ants given the data above.
Input
* Lines 2..A+1: Each line contains a single integer that is an ant type present in the hive
Output
Sample Input
3 5 2 3 1 2 2 1 3
Sample Output
10
Hint
Three types of ants (1..3); 5 ants altogether. How many sets of size 2 or size 3 can be made?
OUTPUT DETAILS:
5 sets of ants with two members; 5 more sets of ants with three members
Source
问题描述: 第一行给出T A S B四个整数
T是指有T种不同的数,A是指这些数的总个数
S和B是指一个区间[S,B] 这个区间的每个整数值x代表这A个数的x个元素的子集
求: 从S到B这些子集的总个数的最后6位(MOD 1000,000)
实际上这就是一个多重集组合数问题:
有n种物品,第i种物品有ai个,不同种类的物品可以互相区分而相同种类的无法区分.
从这些物品中取出m个 有多少取法
每种物品就对应每种数字,相同的数字无法区分, 从这些数字种取S....B个 取法的总数%1000,000是多少
分析:
定义 dp[i+1][j] := 从前i种数取出j个数 共有多少种取法
可以这样考虑:
min(A[i],j)
dp[i+1][j] = Σ dp[i][j-k] * 1
k = 0
(也就是说 从前i-1种取出k个,剩下的从第i种取出,由于第i种的所有数都是相同的 无法区分 所以只有一种取法 在后面*1即可)
这样,枚举i,j,k 复杂度为O(nm²)
而
min(A[i],j) min(j-1,A[i])
Σdp[i][j-k] * 1 = Σdp[i][j-k-1] + dp[i][j](少的一项 k = 0) - dp[j-1-A[i]](多的一项 k = A[i]);
k=0 k=0
那么 把i代换成i+1 j-k代换为k
就得到 dp[i+1][j] = dp[i+1][j-1] + dp[i][j] - dp[j-1-A[i]](如果j-1 >=A[i])
这样复杂度为O(mn)
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
int t,a,s,b;
int dp[5000 + 10][5000 + 10];
int A[5000 + 10];
map<int,int> m;
int main()
{
scanf("%d %d %d %d",&t,&a,&s,&b);
for(int i = 0;i<a;i++)
{
int x;
scanf("%d",&x);
A[x-1]++;
}
// int cnt = 0;
// for(map<int,int>::iterator it = m.begin();it!=m.end();it++)
// {
// A[cnt++] = it->second;
// }
for(int i = 0 ;i<=t;i++)
{
dp[i][0] = 1;
}
for(int i = 0;i<t;i++)
{
for(int j = 1;j<=b;j++)
{
if(j - 1 - A[i] >=0)
{
dp[i+1][j] = (dp[i+1][j-1] + dp[i][j] - dp[i][j-1-A[i]] + 1000000) % 1000000;
}
else
{
dp[i+1][j] = (dp[i+1][j-1] + dp[i][j]) % 1000000;
}
}
}
// dp[i+1][j] 从前i种选择出j个
// min(A[i],j) min(j-1,A[i])
// = Σdp[i][j-k] * 1 = Σdp[i][j-k-1] + dp[i][j](少的一项 k = 0) - dp[j-1-A[i]](多的一项 k = A[i]);
// k=0 k=0
//主要是考虑如何降低复杂度 把Σ变为一维的加减运算
//代换 dp[i+1][j] = dp[i+1][j-1] + dp[i][j] - dp[j-1-A[i]]
long long ans = 0;
for(int i = s;i<=b;i++)
{
ans = (ans + dp[t][i]) % 1000000;
}
printf("%lld\n",ans);
}