E - ∙ (Bullet)(组合数学)

传送门

思路:显然对于一组只有两种情况,同号或者异号, 存在0。

且要使。显然是同号与异号进行组合。

且我们只需考虑除去最大公因数的组合.

因为

所以我们考虑储存同号的个数及对应异号的个数。(这里用实现即可)

对于当前组,假设同号个数为,异号个数为,由于不能同时选同号和异号的,

显然我们可以只选同号或者只选异号的,这样有种情况。

同时包含了当前组一个数不选的情况,所以要减去1.

即对于当前组的贡献为.

所以根据乘法原理有:

1.显然当中只有一个时,等价于进行组合,可以归为.

2.当时,因为它们不能与其他数共存,只能单独存在。所以对于这样的数贡献为

由于集合的非空性:所以还要减去所有组一个数都不选的情况

时间复杂度:

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
int n,cnt;
ll ksm(ll a,ll n){
    ll ans=1;
    while(n){
        if(n&1) ans=ans*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ans;
}
int main(){
    map<pair<ll,ll>,pair<int,int> >mp;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        ll a,b;
        scanf("%lld%lld",&a,&b);
        if(!a&&!b){
            cnt++;
            continue;
        }
        ll g=__gcd(a,b);
        a/=g,b/=g;
        if(b<0) a=-a,b=-b;//保证同号的时候都为正,异号的时候b恒为正. 
        if(a<=0) mp[{b,-a}].second++;//该组同号对应的异号个数. 
        else mp[{a,b}].first++;    //该组同号个数 
    }
    ll ans=1;
    for(auto it:mp){
        ans=(ans*(mod+ksm(2,it.second.first)+ksm(2,it.second.second)-1)%mod)%mod;//防止负数要+mod 
    }
    printf("%lld\n",(mod+ans+cnt-1)%mod);
    return 0;
}