HDU 5000 Clone(离散数学+DP)(2014 ACM/ICPC Asia Regional Anshan Online)

时间:2022-01-31 08:25:42
Problem Description
After eating food from Chernobyl, DRD got a super power: he could clone himself right now! He used this power for several times. He found out that this power was not as perfect as he wanted. For example, some of the cloned objects were tall, while some were short; some of them were fat, and some were thin.

More evidence showed that for two clones A and B, if A was no worse than B in all fields, then B could not survive. More specifically, DRD used a vector v to represent each of his clones. The vector v has n dimensions, representing a clone having N abilities. For the i-th dimension, v[i] is an integer between 0 and T[i], where 0 is the worst and T[i] is the best. For two clones A and B, whose corresponding vectors were p and q, if for 1 <= i <= N, p[i] >= q[i], then B could not survive.

Now, as DRD's friend, ATM wants to know how many clones can survive at most.

 
Input
The first line contains an integer T, denoting the number of the test cases.

For each test case: The first line contains 1 integer N, 1 <= N <= 2000. The second line contains N integers indicating T[1], T[2], ..., T[N]. It guarantees that the sum of T[i] in each test case is no more than 2000 and 1 <= T[i].

 
Output
For each test case, output an integer representing the answer MOD 10^9 + 7.
 
题目大意:定义向量x = {x1, x2, ……, xn}, y = {y1, y2, ……, yn},有x ≥ y当且仅当对于任意 i 都有 xi ≥ yi。如果x ≥ y,那么 y 不能存在。先给一个向量T,问所有小于等于T的向量中,最多有多少个向量可以共存。
思路:既然没有人写题解,我来写一下……
首先,所有小于等于T的向量的集合,是一个偏序关系,偏序关系中的一条反链就是题目中的一个解,而最长反链就是题目中要求的最大解
根据Dilworth定理,最长反链 = 最小链覆盖
我们来看一下题目中的偏序集长什么样:
HDU 5000 Clone(离散数学+DP)(2014 ACM/ICPC Asia Regional Anshan Online)(不要吐槽图歪歪扭扭的)
于是,显然最小链覆盖取决于最多结点的一行。
而每个有边相连的结点,都只是差一个 1 ,所以每一行里的向量的和都是一样的。
所以题目就变成,设和为 x 的数的小于等于T的向量个数为cnt(x),求max{cnt(x), x = 1..sum{Ti}}
本来简单DP出来之后,可以直接取最大值,但是这题求出来的是模的结果,就不行了。
简单打表可以发现,最大的和总是cnt(sum{Ti} / 2),这个是可以证明的,这里不证了……
 
代码(125MS):
 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <numeric>
#include <functional>
using namespace std;
typedef long long LL; const int MAXN = ;
const int MOD = 1e9 + ; void update_add(int &a, int b) {
a += b;
if(a >= MOD) a -= MOD;
} int a[MAXN][MAXN], sum[MAXN][MAXN];
int b[MAXN];
int n, s, T; void solve() {
memset(a, , sizeof(a));
s = accumulate(b, b + n, ); for(int i = ; i <= b[]; ++i) a[][i] = ;
sum[][] = a[][];
for(int j = ; j <= s; ++j) sum[][j] = (sum[][j - ] + a[][j]) % MOD; for(int i = ; i < n; ++i) {
for(int j = ; j <= s; ++j) {
//for(int k = j - b[i]; k <= j; ++k) a[i][j] += a[i - 1][k];
if(j - b[i] <= ) a[i][j] = sum[i - ][j];
else a[i][j] = (sum[i - ][j] - sum[i - ][j - b[i] - ] + MOD) % MOD;
}
sum[i][] = a[i][];
for(int j = ; j <= s; ++j) sum[i][j] = (sum[i][j - ] + a[i][j]) % MOD;
}
} void print() {
for(int j = ; j <= s; ++j)
printf("%4d", j);
puts("");
for(int i = ; i < n; ++i) {
for(int j = ; j <= s; ++j)
printf("%4d", a[i][j]);
puts("");
}
} int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = ; i < n; ++i) scanf("%d", &b[i]);
sort(b, b + n);
solve();
//print();
printf("%d\n", a[n - ][s / ]);
}
}