题解P5123

· · 题解

前言

题意:求两两集合间没有交集的对数。
一看涉及集合计数,考虑容斥原理。

详解

先把答案赋值为 n(n-1),即总共的对数。
枚举 S_i ,答案减去与 S_j(j<i) 有交集的对数。
对于集合 S_i=\{a,b,c\} ,(此处把 m 设为3)
cnt_i 数组为含有i元素的集合个数。 那么与S_i有交集的集合个数为(容斥原理):

用总答案减去上式即可。 $m=5$ 也同理:我们发现上面的 $cnt$ 中的下标对应的是每个元素的出现与不出现。 这样就可以使用二进制枚举的方法。 用二进制中的第 $i$ 位表示第 $i$ 个数的出现与否。 当出现了奇数个元素时,对贡献为正;否则为负。 但是如何存储 $cnt$ 数组呢?用哈希表或 STL ```map``` 即可。 这里使用字符串存储。 ### 代码 ```cpp #include<algorithm> #include<cstdio> #include<iostream> #include<map> using namespace std; long long n,ans; string a[7]; map<string,long long> f; int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { for(int j=1; j<=5; j++) cin>>a[j]; sort(a+1,a+6); for(int j=1; j<32; j++) {//利用二进制枚举哪些数被选 int cnt=0; string s=""; for(int k=1;k<=5;k++) if(j&(1<<(k-1))) { cnt++; s+=a[k]+" "; } //用字符串存储方案 if(cnt&1) ans+=f[s]; else ans-=f[s]; f[s]++; } } printf("%lld\n",n*(n-1)/2-ans); return 0; } ``` 应该是较短的代码。