Problem
给你一个长度为 n n n 的正整数序列 { a n } \{a_n\} {an},它包含 ( 2 n − 1 ) (2^n−1) (2n−1) 个非空子序列,注意到 ( 2 n − 1 ) (2^n−1) (2n−1) 是一个奇数。
我们定义一个子序列的权值为子序列内所有元素权值之和,求所有非空子序列的权值的中位数。
数据范围: 1 ≤ n ≤ 2000 1≤n≤2000 1≤n≤2000, 1 ≤ a i ≤ 2000 1≤a_i≤2000 1≤ai≤2000。
Solution
我们暂且将空序列纳入考虑范围,这样我们会发现子序列的权值具有一种对称性。
具体来说,记所有元素的权值和为 s u m sum sum。我们发现,对于任意一个权值为 x x x( x ≤ ⌊ s u m 2 ⌋ x\le \lfloor \frac {sum} 2\rfloor x≤⌊2sum⌋)的子序列,都存在另一个权值为 s u m − x sum-x sum−x( s u m − x ≥ ⌊ s u m 2 ⌋ sum-x≥\lfloor \frac {sum} 2\rfloor sum−x≥⌊2sum⌋)的子序列。这很显然。
可以看出, ⌊ s u m 2 ⌋ \lfloor \frac {sum} 2\rfloor ⌊2sum⌋ 是一个分隔线,每当一个权值在其本身或左边的子序列,就存在一个权值在其本身或右边的子序列。那么答案应该就在 ⌊ s u m 2 ⌋ \lfloor \frac {sum} 2\rfloor ⌊2sum⌋ 附近。仔细想一想,除去空序列后最小的、大于等于 ⌈ s u m 2 ⌉ \lceil \frac {sum} 2\rceil ⌈2sum⌉ 的子序列权值就是答案。
于是现在的问题就是怎么求这个值了。
我们设 f [ i ] [ j ] f[i][j] f[i][j] 表示前 i i i 个数组成的权值为 j j j 的子序列是否存在。
那么有一个显然的转移: f [ i ] [ j ] = ( f [ i − 1 ] [ j ] ) ∣ ( f [ i − 1 ] [ j − a i ] ) f[i][j]=(f[i-1][j])|(f[i-1][j-a_i]) f[i][j]=(f[i−1][j])∣(f[i−1][j−ai]), a i a_i ai 为第 i i i 个数的权值。
考虑到第一维只与 i i i 和 i − 1 i-1 i−1 有关,可以优化掉。
又由于每次转移都是偏移一段距离后进行或操作,可以使用 b i t s e t bitset bitset 优化时空复杂度。
时间复杂度 O ( n × s u m 64 ) O(\frac{n\times sum}{64}) O(64n×sum),常数极小,可以通过本题。
Code
#include<bitset>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2005
using namespace std;
bitset<N*N>f;
int main(){
int n,x,sum=0;
scanf("%d",&n);
f[0]=1;
for(int i=1;i<=n;++i){
scanf("%d",&x);
f|=(f<<x),sum+=x;
}
for(int i=(sum+1)/2;i<=sum;++i)
if(f[i]) return printf("%d",i),0;
return 0;
}