【题解】ABC172 E. Bullet

本文介绍了如何通过分数配对解决一个关于集合中非空子集的计数问题,通过构造map存储分数并利用欧几里得算法避免double精度误差。关键步骤包括配对操作、特殊处理(0,0)和排除空集。最终给出了O(nlogn)的时间复杂度解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

从集合中选一个非空子集,若满足 a_ia_j+b_ib_j=0i!=j 则不合法。求方案总数。n<=5e5

Solution:

稍作变形:a_i/b_i=-b_j/a_j

做到这里,我们把 a_i/b_i 存入 map ,然后在 map 中查找一个值,计算另一个值即可。每一对的贡献为 2^x+2^y-1

但是我们可以继续变形:

(a_i/b_i)*(a_j/b_j)=-1 ,于是乎将 a_i/b_i 配对即可。

注意 double 可能有误差。我们考虑用 pair<x,y> 存储一个分数,同时保证 x>=0

这里要特判 (0,0) 的情况,因为和任何一对都会不合法。

最后去掉空集的情况。答案为 mul+zero-1 。时间复杂度 O(nlogn)

本题同样因为取模的特判 wa 了很多次。要长记性。

#include<bits/stdc++.h>
#define All(x) x.begin(),x.end()
#define INF 0x3f3f3f3f
#define ll long long
#define double long double
#define pll pair<long long,long long>
using namespace std;
//Task : atcoder abc
//author : cqbzly
const int mod=1e9+7;
int n,m,zero;
vector<pll> a;
set<pll> b;
map<pll,int> value; 
map<pll,bool> vis;
ll fpow(ll x,ll y) {
	ll mul=1;
	for(;y;y>>=1) {
		if(y&1) mul=mul*x%mod;
		x=x*x%mod;
	}
	return mul; 
} 
ll gcd(ll x,ll y) {
	return (y==0)?x:gcd(y,x%y);
}
int main() {
//	freopen("data.in","r",stdin);	
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++) {
		ll x,y; cin>>x>>y;
		if((x==0&&y==0)) zero++;
		else {
			ll z=gcd(x,y);
			x/=z,y/=z;
			if(x<0) x=-x,y=-y;
			value[make_pair(x,y)]++;
			b.insert(make_pair(x,y));
		}
	}
	ll mul(1);
	//search for one
	for(auto x:b) {
		if(vis[x]) continue;
		vis[x]=1;
		pll y=make_pair(-x.second,x.first);
		if(y.first<0) y.first=-y.first,y.second=-y.second;
		vis[y]=1;
		mul=mul*(fpow(2,value[x])+fpow(2,value[y])-1)%mod; 
	}
	mul=(mul+zero-1)%mod;
	if(mul<0) mul+=mod;
	cout<<mul;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值