统计蚂蚁(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;
}