Description

研究证明,有一个因素在两头奶牛能否作为朋友和谐共处这方面比其他任何因素都来得重要——她们是不是喜欢同

一种口味的冰激凌!Farmer John的N头奶牛(2≤N≤50,000)各自列举了她们最喜欢的五种冰激凌口味的清单。为

使这个清单更加精炼,每种可能的口味用一个不超过106的正整数ID表示。如果两头奶牛的清单上有至少一种共同

的冰激凌口味,那么她们可以和谐共处。请求出不能和谐共处的奶牛的对数。

Input

输入的第一行包含N

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

Output

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

Sample Input

4
1 2 3 4 5
1 2 3 10 8
10 9 8 7 6
50 60 70 80 90

Sample Output

4
在这里,奶牛4不能和奶牛1、2、3中的任一头和谐共处,奶牛1和奶牛3也不能和谐共处。

HINT

Source

Gold

Solution

正解是容斥(然而我并不会,只会bitset优化暴力,去找题解学习了一下容斥做法)。
不过bzoj好像调了时限,然后我现在网上找到的所有容斥题解都TLE了(因为网上的题解都用了string,换成hash才能过)。
容斥做法:
显然转化为\(n(n-1)/2-\)和谐对数。
然后和谐对数就5种情况:1个一样的,2个一样的,3个一样的,4个一样的,5个一样的。
用经典的容斥式子:
\[ ans=\sum_{i=1}^5f(i)*(-1)^i \]
\(f(i)\)为一样的对数的个数。
那么\(2^5\)枚举所有取法,容斥一遍即可。
复杂度是相对与用string的小常数\(O(2^5nlogn)\)

用bitset优化一下暴力也跑的飞快,就慢了几百ms。复杂度是小常数的\(O(\frac{n^2logn}{w})\)(要开map不然存不下,或者可以分块求答案就不用开map)。

容斥做法:

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define il inline
#define ull unsigned long long

namespace io {

#define in(a) a = read()
#define out(a) write(a)
#define outn(a) out(a), putchar('\n')

#define I_int ll
inline I_int read() {
    I_int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * f;
}
char F[200];
inline void write(I_int x) {
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
}
#undef I_int

}
using namespace io;

using namespace std;

#define N 50010
#define base 23333333

int n = read();
int a[10];
map<ull,ll>mp;

int main() {
    ll ans = 1ll*n*(n-1ll)/2ll;
    for(int i = 1; i <= n; ++i) {
        for(int j = 0; j < 5; ++j) in(a[j]);
        sort(a,a+5); ll sum = 0;
        for(int k = 1; k < (1 << 5); ++k) {
            int tot = 0; ull s = 0;
            for(int j = 0; j < 5; ++j) 
                if(k&(1<<j)) s = s * base + a[j], ++tot;
            if(tot&1) sum += mp[s]++;
            else sum -= mp[s]++;
        }
        ans -= sum;
    }
    outn(ans);
}

bitset做法

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define il inline
#define ull unsigned long long
 
namespace io {
 
#define in(a) a = read()
#define out(a) write(a)
#define outn(a) out(a), putchar('\n')
 
#define I_int ll
inline I_int read() {
    I_int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * f;
}
char F[200];
inline void write(I_int x) {
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
}
#undef I_int
 
}
using namespace io;
 
using namespace std;
 
#define N 50010
 
int n = read();
int a[N][5];
map<int, bitset<50005> > t;
 
int main() { 
    for(int i = 1; i <= n; ++i) {
        for(int j = 0; j < 5; ++j) {
            in(a[i][j]);
            t[a[i][j]][i] = 1;
        }
    }
    ll ans = 0;
    for(int i = 1; i <= n; ++i) {
        bitset<50005>tmp; tmp.reset();
        for(int j = 0; j < 5; ++j) {
            tmp |= t[a[i][j]];
        }
        ans+=n - tmp.count();
    }
    outn(ans/2ll);
}