E - ∙ (Bullet)(组合数学)
思路:显然对于一组 ( a , b ) (a,b) (a,b)只有两种情况, p o s 1 : pos1: pos1:同号或者异号, p o s 2 : pos2: pos2: a , b a,b a,b存在0。
p o s 1 : pos1: pos1:
且要使 A i A j + B i B j = 0 → A i B i = − B j A j A_iA_j+B_iB_j=0\rightarrow \dfrac{A_i}{B_i}=\dfrac{-B_j}{A_j} AiAj+BiBj=0→BiAi=Aj−Bj。显然是同号与异号进行组合。
e p : i : ( a , b ) , j : ( b , − a ) → a b + b ( − a ) = 0 ep:i:(a,b),j:(b,-a)\rightarrow ab+b(-a)=0 ep:i:(a,b),j:(b,−a)→ab+b(−a)=0
且我们只需考虑 ( a , b ) (a,b) (a,b)除去最大公因数 g c d gcd gcd的组合.
因为 a b = a g c d b g c d \dfrac{a}{b}=\dfrac{\dfrac{a}{gcd}}{\dfrac{b}{gcd}} ba=gcdbgcda。
所以我们考虑储存同号的个数及对应异号的个数。(这里用 m a p map map实现即可)
对于当前组 p a i r i pair_i pairi,假设同号个数为 c n t 1 cnt_1 cnt1,异号个数为 c n t 2 cnt_2 cnt2,由于不能同时选同号和异号的,
显然我们可以只选同号或者只选异号的,这样有 2 c n t 1 + 2 c n t 2 2^{cnt_1}+2^{cnt_2} 2cnt1+2cnt2种情况。
2 c n t 1 2^{cnt_1} 2cnt1和 2 c n t 2 2^{cnt_2} 2cnt2同时包含了当前组一个数不选的情况,所以要减去1.
即对于当前组的贡献为 p a i r i = 2 c n t 1 + 2 c n t 2 − 1 pair_i=2^{cnt_1}+2^{cnt_2}-1 pairi=2cnt1+2cnt2−1.
所以根据乘法原理有:
a n s = p a i r 1 × p a i r 2 ⋯ × p a i r n ans=pair_1\times pair_2\dots\times pair_n ans=pair1×pair2⋯×pairn
p o s 2 : pos2: pos2:
1.显然当 a , b a,b a,b中只有一个 0 0 0时,等价于 ( 1 , 0 ) (1,0) (1,0)与 ( 0 , 1 ) (0,1) (0,1)进行组合,可以归为 p o s 1 pos1 pos1.
2.当 a = b = 0 a=b=0 a=b=0时,因为它们不能与其他数共存,只能单独存在。所以对于这样的数贡献为 c n t a = b = 0 cnt_{a=b=0} cnta=b=0
由于集合的非空性:所以还要减去所有组一个数都不选的情况
∴ a n s = p a i r 1 × p a i r 2 ⋯ × p a i r n + c n t a = b = 0 − 1 \therefore ans=pair_1\times pair_2\dots\times pair_n+cnt_{a=b=0}-1 ∴ans=pair1×pair2⋯×pairn+cnta=b=0−1
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
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;
}