1. 程式人生 > 實用技巧 >聯賽膜你測試24 答題 題解

聯賽膜你測試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;
}