统计蚂蚁(ants) \operatorname{统计蚂蚁(ants)} 统计蚂蚁(ants)
题目链接: SSL比赛 1508 \operatorname{SSL比赛\ 1508} SSL比赛 1508
题目
蚂蚁山上有 T ( 1 < = T < = 1 , 000 ) T(1<=T<=1,000) T(1<=T<=1,000) 种蚂蚁,标记为 1.. T , 1..T, 1..T, 每种蚂蚁有 N i N_i Ni 只蚂蚁 ( 1 < = N i < = 100 ) , (1<=N_i<=100), (1<=Ni<=100), 现有 A ( A < = 5000 ) A(A<=5000) A(A<=5000) 只蚂蚁,从中选出 S , S + 1 , … , B ( 1 < = S < = B < = A ) S,S+1,…,B(1<=S<=B<=A) S,S+1,…,B(1<=S<=B<=A) 只蚂蚁一共有多少种选法?
如有 5 5 5 只蚂蚁分别为 { 1 , 1 , 2 , 2 , 3 } , \{1,1,2,2,3\}, { 1,1,2,2,3}, 一共有 3 3 3 种蚂蚁,每一种蚂蚁的数量分别为 2 2 2 , 2 2 2 , 1 1 1 ,以下是选不同数量蚂蚁的方法:
-
1 1 1 个蚂蚁 3 3 3 种选法 : { 1 } { 2 } { 3 } :\{1\}\{2\}\{3\} :{ 1}{ 2}{ 3}
-
2 2 2 个蚂蚁 5 5 5 种选法 : { 1 , 1 } { 1 , 2 } { 1 , 3 } { 2 , 2 } { 2 , 3 } :\{1,1\}\{1,2\}\{1,3\}\{2,2\}\{2,3\} :{ 1,1}{ 1,2}{ 1,3}{ 2,2}{ 2,3}
-
3 3 3 个蚂蚁 5 5 5 种选法 : { 1 , 1 , 2 } { 1 , 1 , 3 } { 1 , 2 , 2 } { 1 , 2 , 3 } { 2 , 2 , 3 } :\{1,1,2\}\{1,1,3\}\{1,2,2\}\{1,2,3\}\{2,2,3\} :{ 1,1,2}{ 1,1,3}{ 1,2,2}{ 1,2,3}{ 2,2,3}
-
4 4 4 个蚂蚁 3 3 3 种选法 : { 1 , 2 , 2 , 3 } { 1 , 1 , 2 , 2 } { 1 , 1 , 2 , 3 } :\{1,2,2,3\}\{1,1,2,2\}\{1,1,2,3\} :{ 1,2,2,3}{ 1,1,2,2}{ 1,1,2,3}
-
5 5 5 个蚂蚁 1 1 1 种选法 : { 1 , 1 , 2 , 2 , 3 } :\{1,1,2,2,3\} :{ 1,1,2,2,3}
你的任务是从中选 S . . B S..B S..B 只蚂蚁的方法总和。
输入
第一行: 4 4 4 个空格隔开的整数: T , A , S T, A, S T,A,S 和 B B B ;
第 2 2 2 到 A + 1 A+1 A+1 行:每行一个整数表示蚂蚁的种类。
输出
输出从 A A A 只蚂蚁中选出 S . . B S..B S..B 只蚂蚁的方法数,答案保留后 6 6 6 位。
样例输入
3 5 2 3
1
2
2
1
3
样例输出
10
数据范围
对于 30 % 30\% 30% 的数据: T < = 30 , A < = 100 ; T<=30,A<=100; T<=30,A<=100;
对于 50 % 50\% 50% 的数据: T < = 100 , A < = 400 ; T<=100,A<=400; T<=100,A<=400;
对于 100 % 100\% 100% 的数据: T < = 1000 , A < = 5000 ; T<=1000,A<=5000; T<=1000,A<=5000;
思路
这道题是一道 dp 。
可以看出蚂蚁放的位置没有太大关系,只会算作一个,就默认按种类编号从小到大放。
(因为是组合)
就设 f [ i ] [ j ] f[i][j] f[i][j] 为选完第 i i i 种蚂蚁,现在一共有 j j j 只的方案数。
那就枚举一个 k k k 表示第 i i i 种蚂蚁放多少个过去,那转移就是这样:
f [ i ] [ j ] = f [ i ] [ j ] + f [ i − 1 ] [ j − k ] f[i][j]=f[i][j]+f[i - 1][j - k] f[i][j]=f[i][j]+f[i−1][j−k]
(不过我用的是 f [ i ] [ j + k ] = f [ i ] [ j + k ] + f [ i − 1 ] [ j ] f[i][j+k]=f[i][j+k]+f[i-1][j] f[i][j+k]=f[i][j+k]+f[i−1][j] ,都可以)
接着我们会发现超时,应该是爆内存(不太清楚,应该会)
那我这里就用了快读和 r e g i s t e r register register 来减时间,用滚动数组来减内存。
正解也是用滚动数组来减内存,但是好像减时间用的是前缀和。(可能我的方法可以卡过去吧)
前缀和就可以缩掉枚举的 k k k ,而滚动数组可以让 i i i 这一纬只用开到 2 2 2 的大小。
最后就枚举蚂蚁总数求出答案,就可以了。
代码
#include<cstdio>
#include<cstring>
#define ll long long
#define rr register
#define mo 1000000
using namespace std;
ll t, a, s, b, x, num[1001];
ll f[2][5001], ans;
ll read() {
//快读
ll an = 0, zhengfu = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') zhengfu = -zhengfu;
c = getchar();
}
while (c >= '0' && c <= '9') {
an = an * 10 + c - '0';
c = getchar();
}
return an * zhengfu;
}
int main() {
t = read();//读入
a = read();
s = read();
b = read();
for (rr ll i = 1; i <= a; i++) {
x = read();
num[x]++;//记录每一种蚂蚁有多少个
}
f[0 & 1][0] = 1;//初始化
for (rr ll i = 1; i <= t; i++) {
//枚举种数
memset(f[i & 1], 0, sizeof(f[i & 1]));//滚动数组
for (rr ll j = 0; j <= b; j++)//枚举蚂蚁总数
for (rr ll k = 0; k <= num[i] && k + j <= b; k++)//枚举这种蚂蚁有多少个
f[i & 1][j + k] = (f[i & 1][j + k] + f[(i - 1) & 1][j]) % mo;//dp转移
}
for (rr ll i = s; i <= b; i++)
ans = (ans + f[t & 1][i]) % mo;//求出答案
printf("%lld", ans);//输出
return 0;
}