联赛膜你测试24 答题 题解
前言:
我严重怀疑这题是个卡常题。。。
最后把sort换成stable_sort才过去,人都傻了
解析:
首先要看对题啊,
上来整个双重否定句,欺负我语文烂?那我也没办法嘤嘤嘤
大概意思就是,给你n个数,这些数显然可以组成\(2^n\) 个数(每个数选或不选),要你选一个数,问至少要多大才能够有不多于(1<<n)*(1-p) 个数比你的数大。
。。。。好像还是很绕
总之就是这样啦
首先考虑爆搜,可以拿到30分的好成绩,因为n>20以后,\(O(2^n)\)的复杂度就萎了。那这时怎么办呢?
可以考虑类似于折半搜索的方法,先处理出前n/2个数能够拼出的所有数,再处理出后n/2个数能够拼出的所有数,最后再二分答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1500000+10;
const double eps=1e-9;
int b[50];
ll a[maxn],c[maxn];
int n,tot,cnt;
double p;
ll ss;
void dfs(int now,ll w){
if(now>n){
a[++tot]=w;
return;
}
dfs(now+1,w);
dfs(now+1,w+b[now]);
}
bool cmp(int x,int y){
return x<y;
}
void Solve1(){
dfs(1,0);
sort(a+1,a+tot+1,cmp);
double x=((double)tot*p);
int y=(int)x;
if(x-y>eps) y++;
printf("%lld\n",a[y]);
}
void dfs2(int now,ll w){
if(now>(n/2)){
a[++tot]=w;
return;
}
dfs2(now+1,w);
dfs2(now+1,w+b[now]);
}
void dfs3(int now,ll w){
if(now>n){
c[++cnt]=w;
return;
}
dfs3(now+1,w);
dfs3(now+1,w+b[now]);
}
bool check(int x){
int head=1;
ll res=0;
for(register int i=cnt;i;--i){
while(c[i]+a[head]<=x){
if(head>tot) break;
head++;
}
if(head>tot) break;
res+=tot-head+1;
}
return res<ss ;
}
void Solve2(){
dfs2(1,0);
dfs3(n/2+1,0);
stable_sort(a+1,a+tot+1,cmp);
stable_sort(c+1,c+cnt+1,cmp);
double s=(double)(1ll<<n)*(1.0-p);
ss=(ll)s;
if(s-ss>eps) ss++;
int l=1,r=a[tot]+c[cnt];
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)) r=mid-1;
else l=mid+1;
}
printf("%d\n",l);
}
void Solve(){
scanf("%d%lf",&n,&p);
for(register int i=1;i<=n;++i) scanf("%d",&b[i]);
if(n<=20){
Solve1();
return;
}
Solve2();
}
int main(){
freopen("answer.in","r",stdin);
freopen("answer.out","w",stdout);
Solve();
return 0;
}