2018华工校赛_K-小马哥的超级盐水(折半枚举+二分搜索+数学)

小马哥的超级盐水

时间限制:C/C++ 5秒,其他语言10秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld

题目描述:

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

输入描述:

输入第一行包含一个整数 T ,代表数据组数。
每组数据第一行包含三个整数 n , x , y ( 2 x 10000 , m a x ( x , 1 ) 10000 ) , g c d ( x , y ) = 1 , g c d ( x , y ) 表示 x y 的最大公约数。
接下来 n 行,第行包含两个整数 a i , b i ( 0 x 10000 , m a x ( x , 1 ) 10000 )

输出描述:

每组数据输出一行,包含一个整数表示非空子集的个数。

样例:

输入

1
5 1 2
1 2
1 2
1 2
1 2
1 4

输出

15

题解:

这题一开始用深搜暴力果然超时了,在现场做了两小时都做不出来,没有对分式进一步进行拆分。回来之后别人给了我提示,实际上对于 a + a i b + b i = x y 我们可以变成 a y b x = b i x a i y ,再用折半枚举法这样用时就是最多 2 17 那肯定不会超时了。
这里我先枚举了一半储存起来,然后另外一半我每枚举一个就二分搜索一次上限和下限就知道有多少个符合的,再加到Ans里面就完成了。(感觉我说的挺轻松的…)。

代码:

#include<bits/stdc++.h>
using namespace std;
struct node
{
    long long x,y;
}ans[40];
long long c[1<<19];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        long long n,x,y;
        cin>>n>>x>>y;
        for(int i=0;i<n;i++)
        {
            scanf("%lld%lld",&ans[i].x,&ans[i].y);
        }
        int n1=n/2;
        int n2=n-n1;
        int m1=1<<n1;
        int m2=1<<n2;
        long long Ans=0;
        for(int i=0;i<m2;i++)
        {
            long long t1=0,t2=0;
            for(int j=0;j<n2;j++)
            {
                if((i>>j)&1)
                {
                    t1+=ans[j+n1].x;
                    t2+=ans[j+n1].y;
                }
            }
            c[i]=x*t2-y*t1;
        }
        sort(c,c+m2);
        for(int i=0;i<m1;i++)
        {
            long long t1=0,t2=0;
            for(int j=0;j<n1;j++)
            {
                if((i>>j)&1)
    0           {
                    t1+=ans[j].x;
                    t2+=ans[j].y;
                }
            }
            long long temp=y*t1-x*t2;
            Ans+=upper_bound(c,c+m2,temp)-lower_bound(c,c+m2,temp);
//          cout<<Ans<<endl;
        }
        printf("%lld\n",Ans-1);
     } 
 } 

猜你喜欢

转载自blog.csdn.net/weixin_40859716/article/details/79847339
今日推荐