前言
这哪里是模拟赛这就是爆〇赛
每道题都有思路(X)
每道题都只能暴力()
题面
给你一个长度为 的正整数序列 ,它包含
2
n
−
1
2^n-1
2n−1个非空子序列,注意到
2
n
−
1
2^n-1
2n−1是一个奇数。
我们定义一个子序列的权值为子序列内所有元素权值之和。
求所有非空子序列的权值的中位数。
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 21∑i=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(64N∗sum),据说常数很小所以随便过。
#include<bits/stdc++.h>
#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;}
}