提供一个正确的 bitset
做法。
首先可以将冰激凌的数字离散化。
然后发现直接求不能和谐共处的方案数不好求,我们考虑反过来求能和谐相处的个数。
设能与第 头奶牛和谐相处的奶牛的个数为 (包括自己),那要求的答案即为 。
我们可以想到对于每种冰激凌开一个大小为 的 bitset
,记录有哪些奶牛喜欢这种冰激凌,那么 就等于这头奶牛喜欢的五种冰激凌所代表的 bitset
或起来的 位数。
然后这样求时间复杂度为 ,空间复杂度为 ,无法通过。
我们发现其中有很多的 bitset
都没有存什么东西,浪费了很多空间,所以我们考虑根号分治。
具体来讲,如果一个冰激淋的出现次数 ,那么对于这种冰激凌开一个 bitset
;否则,代替 bitset
,对于这种冰激凌开一个 vector
。(也是记录有哪些奶牛喜欢这种冰激凌)
查询时,将对应的 bitset
或起来,再把对应的 vector
里面的奶牛暴力加进去。
单次查询时,处理 bitset
时间复杂度为 ,因为我们限制开了 vector
的冰激凌出现次数 ,vector
暴力添加时间复杂度为 ,总共为 。
因为我们限制开了 bitset
的冰激凌至少出现 次,所以至多会开 个 bitset
,且 vector
最多总共占用 的空间,空间复杂度为 。
如果实现优秀甚至可以吊打一大堆大常数 ……
代码主体部分:
// 懒惰的作者并没有离散化
// lim 为 sqrt(n)
int n, m, cnt, a[50001][6], t[1000001], id[1000001];
vector<int> v[1000001];
bitset<50001> b[lim + 1], c;
ll ans;
int main(){
read(n);
for (int i = 1;i <= n;i ++){
for (int j = 1;j <= 5;j ++) read(a[i][j]), m = max(m, a[i][j]), t[a[i][j]] ++;
}
for (int i = 1;i <= m;i ++){
if (t[i] > lim) id[i] = ++ cnt;
}
for (int i = 1;i <= n;i ++){
for (int j = 1;j <= 5;j ++){
if (t[a[i][j]] > lim) b[id[a[i][j]]][i] = 1;
else v[a[i][j]].push_back(i);
}
}
for (int i = 1;i <= n;i ++){
c.reset();
for (int j = 1;j <= 5;j ++){
if (t[a[i][j]] > lim) c |= b[id[a[i][j]]];
else {
for (int x : v[a[i][j]]) c[x] = 1;
}
}
ans += c.count() - 1;
}
write(n * (n - 1ll) / 2 - ans / 2);
}