(折半枚举法)
题意:小马哥有杯盐水,第杯有单位的盐和单位的水。小马哥很无聊,于是他想知道有多少种这杯盐水的非空子集,倒在一起之后盐和水的比是
解法:折半枚举法,因为是求能组合出的所有情况,因此对于每一杯盐水都只有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; }