【校内模拟】【19-07-25】中位数 【结论】

前言

这哪里是模拟赛这就是爆〇赛
每道题都有思路(X)
每道题都只能暴力()

题面

给你一个长度为 的正整数序列 ,它包含 2 n − 1 2^n-1 2n1个非空子序列,注意到 2 n − 1 2^n-1 2n1是一个奇数。
我们定义一个子序列的权值为子序列内所有元素权值之和。
求所有非空子序列的权值的中位数。
N<=2000,ai<=2000

题解

暴力想法:

算出每个子序列的权值取中位数

期望得分10pts左右?

我的想法:

考虑到每加入一个数,新增的子序列权值就是原来的所有加上这个数,然后再把这个数丢进去,模拟一下即可。

期望得分???

正解想法:

如果我们把空集也算为子序列,那么对于每一个权值为k的子序列,一定有一个权值为 ∑ i = 1 n a i \sum_{i=1}^n ai i=1nai − k -k k,也就是说,现在所有的子序列的权值关于 1 2 ∑ i = 1 n a i \frac{1}{2} \sum_{i=1}^n ai 21i=1nai 对称。

由于我们不考虑空集,所以我们要找的就是比那个对称轴(懒得打了)刚好大一点点的值

然后因为直接算不太方便,再加上这个优秀的数据范围,我们就考虑dp一下,设计 d p [ i ] [ j ] dp[i][j] dp[i][j]为前i个数能否凑出 j j j,转移很显然就不说了。

为了避免空间爆炸我们就用bitset,时间复杂度为 O ( N ∗ s u m 64 ) O(\frac{N*sum}{64}) O(64Nsum),据说常数很小所以随便过。

#include
#define rint register int
#define ivoid ilnine void
#define iint inline int
#define endll '\n'
#define ll long long
using namespace std;
int n;
int a[2005],sum;
bitset<2000010> f;

iint rad()
{
	int x=0,f=1;char c;
 	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
 	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
 	return x*f; 
}

signed main()
{
	// freopen("a.in","r",stdin);
	// freopen("a.out","w",stdout);
 	n=rad();
 	for(rint i=1,k;i<=n;i++)a[i]=rad(),sum+=a[i];
 	f[0]=1;
 	for(rint i=n;i>=1;i--)f|=f<<a[i];
 	//这简直就是骚写…… 
 	//你可以想象是把数组进行拖动然后按上去之类的
 	//反正是要快很多
  	for(rint i=(sum+1)>>1;i<=sum;i++;
	if(f[i]){printf("%d",i);return 0;}
}

你可能感兴趣的:(校内模拟)