【2019/07/25测试 T1】中位数

传送门


Problem

给你一个长度为 n n n 的正整数序列 { a n } \{a_n\} {an},它包含 ( 2 n − 1 ) (2^n−1) (2n1) 个非空子序列,注意到 ( 2 n − 1 ) (2^n−1) (2n1) 是一个奇数。

我们定义一个子序列的权值为子序列内所有元素权值之和,求所有非空子序列的权值的中位数。

数据范围: 1 ≤ n ≤ 2000 1≤n≤2000 1n2000 1 ≤ a i ≤ 2000 1≤a_i≤2000 1ai2000


Solution

我们暂且将空序列纳入考虑范围,这样我们会发现子序列的权值具有一种对称性。

具体来说,记所有元素的权值和为 s u m sum sum。我们发现,对于任意一个权值为 x x x x ≤ ⌊ s u m 2 ⌋ x\le \lfloor \frac {sum} 2\rfloor x2sum)的子序列,都存在另一个权值为 s u m − x sum-x sumx s u m − x ≥ ⌊ s u m 2 ⌋ sum-x≥\lfloor \frac {sum} 2\rfloor sumx2sum)的子序列。这很显然。

可以看出, ⌊ 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[i1][j])(f[i1][jai]) a i a_i ai 为第 i i i 个数的权值。

考虑到第一维只与 i i i i − 1 i-1 i1 有关,可以优化掉。

又由于每次转移都是偏移一段距离后进行或操作,可以使用 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值