牛客网-“景驰科技杯”2018年华南理工大学程序设计竞赛 K-小马哥的超级盐水

题目链接

(折半枚举法)

题意小马哥有杯盐水,第杯有单位的盐和单位的水。小马哥很无聊,于是他想知道有多少种这杯盐水的非空子集,倒在一起之后盐和水的比是

解法:折半枚举法,因为是求能组合出的所有情况,因此对于每一杯盐水都只有2种状态,取和不取,但是n_max=35,2^35的运算肯定超时,因此需要进行一波折半操作,对于每n杯盐水,我们分为2份,每份最大的讨论情况为2^18<1e6,因此我们可以先分别处理出2部分的所有组合情况,然后选择小部分中的每个数据到另一份数据中进行二分查找,找到满足组合之后值为x/y的数量即可时间复杂度O(nlogn)(这里的n是枚举所有情况的n,不是题目的n了)

代码如下

#include<cmath>  
#include<cstdlib>  
#include<string>  
#include<cstring>  
#include<algorithm>  
#include<stdio.h>  
#include<cmath>  
#include<iostream>  
using namespace std;
#define ll long long  
const int maxn = 3e5 + 500;
int n, top1, top2;
ll ans, xx, yy;
struct node
{
	ll  x, y;
}a[maxn];
ll s1[maxn], s2[maxn];
void dfs1(int i, ll x, ll y)
{
	if (i == n / 2) {
		s1[top1++] = xx*y - yy*x;
		return;
	}
	dfs1(i + 1, x, y);
	dfs1(i + 1, x + a[i].x, y + a[i].y);
}
void dfs2(int i, ll x, ll y)
{
	if (i == n) {
		s2[top2++] = yy*x - xx*y;
		return;
	}
	dfs2(i + 1, x, y);
	dfs2(i + 1, x + a[i].x, y + a[i].y);
}
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%lld%lld", &n, &xx, &yy);
		for (int i = 0; i < n; i++)
			scanf("%lld%lld", &a[i].x, &a[i].y);
		top1 = 0; dfs1(0, 0, 0);
		top2 = 0; dfs2(n / 2, 0, 0);
		ans = 0;
		// x     s1.x+s2.x  
		//--- = ------------  => x*s1.y+x*s2.y=y*s1.x+y*s2.x=> x*s1.y-y*s1.x = y*s2.x-x*s2.y  
		// y     s1.y+s2.y  
		sort(s2, s2 + top2);
		for (int i = 0; i < top1; i++)
			ans += (upper_bound(s2, s2 + top2, s1[i]) - s2) - (lower_bound(s2, s2 + top2, s1[i]) - s2);
		cout << ans - 1 << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/80207925
今日推荐