统计蚂蚁(ants)

统计蚂蚁(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 1 个蚂蚁 3 3 3 种选法 : { 1 } { 2 } { 3 } :\{1\}\{2\}\{3\} :{ 1}{ 2}{ 3}

  2. 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 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 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 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[i1][jk]
(不过我用的是 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[i1][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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/108149620