来源:牛客网
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
输入描述:
输出描述:
每组数据输出一行,包含一个整数表示非空子集的个数。
输入
1 5 1 2 1 2 1 2 1 2 1 2 1 4
输出
15
2^35次方暴力肯定超时,但是用用分治,将问题1分为2,那么2^17和2^18次方这就不会超时了,题目给了5s。对分成的两部分进行dfs,对于一瓶盐水,都有拿和不拿两个状态,dfs正常思路就可以了 。比较麻烦的是,如何处理x/y的问题,其实这个问题就是将一棵树,分为两颗子树i,j,先对i进行操作,满足ai/bi==x/y 即 ai*y-bi*x==0,这就是满足条件的,而且不满足条件的ai*y-bi*x也要记录下来,因为减法肯定有正有负,因此我们不能用数组,要用map(映射) ,先把所有i树的所有条件都记录下来,那么接下来对j树的处理就是关键了。————大佬教我的!!!
首先我们先看,j树直接符合x/y的,假如说i树有n种,j树有m种,那么单纯符合x/y的组合就有n*m种,但是我们条件不能直接相乘,我们应该用加例如说,对于i树 ,f[0]=3,我们找到一个符合的值那么我们应该用sum+=f[0],如果在找到一个,那么继续sum+=f[0]。
接着,我们看如果两瓶盐水单独不符合,但是混合后符合的情况,i树元素和j树元素如果(ai+aj)/(bi+bj)==x/y 那么我们就有ai*y-bi*x==-aj*y+bj*x ,因此只要满足这个条件我们就可以混合起来,算一种了。也就是说,当求出j树的一个元素为-aj*y+bj*x, 那么f[-aj*y+bj*x]就是找到它对应在i树里混合后等于x/y的值,也就是f[ai*y-bi*x],那么直接sum+=f[-aj*y+bj*x]就可以得到它们总共混合后的种类数,其实也就是相当于上面的n*m。因此j树的边界操作应该是sum+=f[-aj*y+bj*x].
最后一点就是,我们这样做在第i树集合里面我们会有一种情况就是所有水都不取,j树也一样,那么这就是空集了,因此我们算出来的总个数还要-1才是所求的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#define ll long long
using namespace std;
map<ll,ll>f;
int n,m,x,y;
ll a[40],b[40],sum;
void dfs(ll v1,ll v2,int step)
{
if(step==m)
{
f[v1*y-v2*x]++;
return;
}
dfs(v1+a[step],v2+b[step],step+1);
dfs(v1,v2,step+1);
}
void DFS(ll v1,ll v2,int step)
{
if(step==n)
{
sum+=f[-v1*y+v2*x];
return;
}
DFS(v1+a[step],v2+b[step],step+1);
DFS(v1,v2,step+1);
}
int main()
{
int t,i;
scanf("%d",&t);
while(t--)
{
f.clear();
sum=0;
scanf("%d%d%d",&n,&x,&y);
for(i=0; i<=n-1; i++)
scanf("%d%d",&a[i],&b[i]);
m=n/2;
dfs(0,0,0);
DFS(0,0,m);
printf("%lld\n",sum-1);
}
}