题目
题目链接:http://poj.org/problem?id=3046
题目来源:《挑战》练习题
简要题意: T 种编号的蚂蚁,第
i 个数为 Ni ,问能够构成多少种秩在 [S,B] 之间的集合有多少。数据范围: 1⩽T⩽1000;1⩽Ni⩽100
题解
首先肯定是映射成 Ni 。
普通的想法是搞 dp[i][j] ,前 i 个秩为
j 的集合的个数。则可以得到方程 dp[i][j]=∑k=max(0,j−Ni)jdp[i−1][k]
这个方程去直接搞的话肯定是TLE的咯,想办法优化就是了。
dp[i][j] 做成前缀和就行了,前 i 个秩不超过
j 的集合的个数,然后区间求和去转移,方程如下:
dp[i][j]={dp[i][j−1]+dp[i−1][j]−dp[i−1][j−Ni−1] j>Nidp[i][j−1]+dp[i−1][j] else
实现
需要开滚动数组,写的时候可以对上界进行优化。
可以从小到大排个序,整体效率会变好,可以忽略 0 <script type="math/tex" id="MathJax-Element-15">0</script>个的情况。
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <set>
#include <map>
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define sz(x) ((int)(x).size())
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
LL powmod(LL a,LL b, LL MOD) {LL res=1;a%=MOD;for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
// head
const int N = 1005;
const int M = 1E5+5;
const int MOD = 1e6;
int a[1005];
int dp[2][M];
int main()
{
int n, m, l, r, x;
scanf("%d%d%d%d", &n, &m, &l, &r);
for (int i = 0; i < m; i++) {
scanf("%d", &x);
a[x]++;
}
dp[0][0] = dp[1][0] = 1;
sort(a+1, a+n+1);
int pre = 0, cur = 1, sum = 0;
for (int i = 1; i <= n; i++) {
if (!a[i]) continue;
for (int j = 1; j <= a[i]; j++) {
dp[pre][sum+j] = dp[pre][sum];
}
sum += a[i];
for (int j = 1; j <= a[i]; j++) {
dp[cur][j] = (dp[cur][j-1] + dp[pre][j]) % MOD;
}
for (int j = a[i]+1; j <= sum; j++) {
dp[cur][j] = (dp[cur][j-1] + dp[pre][j] + MOD - dp[pre][j-a[i]-1]) % MOD;
}
swap(cur, pre);
}
printf("%d\n", (dp[pre][r] + MOD - dp[pre][l-1]) % MOD);
return 0;
}