洛谷 P4799 [CEOI2015 Day2]世界冰球锦标赛

Description

P4799 [CEOI2015 Day2]世界冰球锦标赛

Solution

折半搜索好题。

发现到 \(n \leq 2 ^ {20}\) 时,可以直接爆搜出答案,而数据范围是 \(n \leq 2 ^ {40}\),所以我们考虑折半搜索。

具体来说,把前 20 场和后 20 场分别爆搜出答案。

如何爆搜呢?既然是要爆搜,那么爆搜就完了,枚举到一场比赛时,看或者不看都搜索一遍即可(可以看下方代码理解一下)。

搜索完之后如何合并呢?

这个也很简单,我们假设前 20 场存到了 \(a\) 数组中,后 20 场存到了 \(b\) 数组中。

我们对 \(b\) 数组从小到大排序。枚举 \(a\) 数组,然后在 \(b\) 数组中二分答案,查找有多少汾河条件的观看方式,累加即可。

时间复杂度 \(O(2^{\frac{n}{2}} + nlog_n)\)

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long

using namespace std;

ll n, m, cnta, cntb;
ll t[50], a[1 << 21], b[1 << 21];

inline void dfs(ll l, ll r, ll sum, ll a[], ll &cnt){
	if(sum > m) return;
	if(l > r){
		a[++cnt] = sum;
		return;
	}
	dfs(l + 1, r, sum + t[l], a, cnt);
	dfs(l + 1, r, sum, a, cnt);
}

signed main(){
	scanf("%lld%lld", &n, &m);
	for(ll i = 1; i <= n; i++)
		scanf("%lld", &t[i]);
	ll mid = n >> 1;
	dfs(1, mid, 0, a, cnta);
	dfs(mid + 1, n, 0, b, cntb);
	sort(a + 1, a + 1 + cnta);
	sort(b + 1, b + 1 + cntb);
	ll ans = 0;
	for(ll i = 1; i <= cnta; i++)
		ans += upper_bound(b + 1, b + 1 + cntb, m - a[i]) - b - 1;
	printf("%lld\n", ans);
	return 0;
}

End

posted @ 2021-10-08 09:10  xixike  阅读(19)  评论(0编辑  收藏  举报