第一次排位赛 D-Cowpatibility(bitset暴力/位运算枚举子集)

题目描述
研究证明,有一个因素在两头奶牛能否作为朋友和谐共处这方面比其他任何因素都来得重要——她们是不是喜欢同一种口味的冰激凌!

Farmer John 的 NN 头奶牛( 2 ≤ N ≤ 5 × 1 0 4 {2\leq N\leq 5×10^4\\} 2N5×104)各自列举了她们最喜欢的五种冰激凌口味的清单。为使这个清单更加精炼,每种可能的口味用一个不超过 1 0 6 10^6 106 的正整数 ID 表示。如果两头奶牛的清单上有至少一种共同的冰激凌口味,那么她们可以和谐共处。
请求出不能和谐共处的奶牛的对数。

输入格式:
输入的第一行包含 N。以下 N 行每行包含5个整数(各不相同),表示一头奶牛最喜欢的冰激凌口味。

输出格式:
输出不能和谐共处的奶牛的对数。

题解1(bitsset暴力):
思路: O ( n 2 ) O(n^2) O(n2)暴力求解,外循环遍历所有牛,内循环求和谐数。



























显然会tle,外循环无法降复杂度,所以要降低求和谐的复杂度,用上bitset,求1的个数来求和谐数,复杂度 O ( n l o g ( n ) 32 ) O(\frac{nlog(n)}{32}) O(32nlog(n)),要实现这个的话就要先预处理,求出每种冰激凌有哪些牛喜欢吃。复杂度 O ( n 2 l o g ( n ) 32 ) O(\frac{n^2log(n)}{32}) O(32n2log(n))
代码:

#include <bits/stdc++.h>
using namespace std;
const int M=5e4+10;
map<int,bitset<M> > mp;
int n,ans=0,a[M][10];
int main(){
	cin>>n;
	for(int i=0;i<n;i++)
		for(int j=0;j<5;j++){
			scanf("%d",&a[i][j]);
			mp[a[i][j]].set(i);//将第i位(从0开始)变为1,表示a[i][j]冰激凌第i头牛喜欢吃
		}
	for(int i=0;i<n;i++){
		bitset<M> p;
		for(int j=0;j<5;j++)
			p|=mp[a[i][j]];//p的第k位为1表示i跟k和谐
		ans+=n-p.count();//不和谐的数量
	}
	cout<<ans/2;//求了两次,所以要除以2
	return 0;
}

题解2(位运算枚举子集):
思路:用string记录每头牛喜欢的冰激凌的集合,map<string,int>记录集合的出现的次数,对每头牛,统计在他前面有多少跟他和谐,根据容斥原理,集合中冰激凌个数为奇数时累加,偶数时减。复杂度 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int M=5e4+10;
map<string,ll> f;
ll n,ans=0;
string a[10];
int main(){
	cin>>n;
	for(ll i=0;i<n;i++){
		for(ll j=0;j<5;j++) cin>>a[j];
		sort(a,a+5);
		//排序,保证集合相同时不会因顺序不同而表示成不同集合,这里是按字典序排序,数字不一定从小到大,在这里效果一样
		for(ll bits=1;bits<(1<<5);bits++){//枚举子集
			string s;
			ll cnt=0;//统计子集冰激凌个数
			for(ll k=0;k<5;k++)
				if(bits&(1<<k))//子集中有第k个冰激凌
					cnt++,s+="_"+a[k];//防止出现1 12与12 1表示成一样的集合的情况
			if(cnt&1) ans+=f[s];//容斥原理
			else ans-=f[s];
			f[s]++;//加上自身
		}
	}
	cout<<(n-1)*n/2-ans;//总共有C(n,2)个,减去和谐的数
	return 0;
}

第二种解法复杂度比第一种低,但是,实际运行时间第一种是451ms,第二种1762 ms,这是因为第一种里bitset快,第二种map,string拖慢了速度。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值