bzoj3198
容斥原理+哈希表
恰好k个,那么上容斥原理,我们先2^6枚举相同的位置,用哈希表判断有多少个对应位置相同的元素,然后用容斥原理计算,似乎这里的容斥没有那么简单,详见这里 http://www.cnblogs.com/candy99/p/mobius.html, 要乘上组合数计算
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int N = 100010, mod = 100007; int n, k; ll ans; int c[8][8], a[N][6]; namespace Hash_List { int cnt, tot; int h[N], bel[N]; struct List { int nxt, id, cnt; } e[N << 6]; void ini() { ++cnt; } bool same(int x, int y, int s) { for(int i = 0; i < 6; ++i) if(s & (1 << i) && a[x][i] != a[y][i]) return false; return true; } int hash(int *a, int s) { int h = 0; for(int i = 0; i < 6; ++i) if(s & (1 << i)) h = ((h * 12321 % mod + a[i] % mod) % mod + mod) % mod; return h; } int insert(int id, int s) { int val = hash(a[id], s); if(bel[val] != cnt) h[val] = 0, bel[val] = cnt; // 如果这个没有被标记过,那么就暴力查找,否则直接插入 for(int i = h[val]; i; i = e[i].nxt) if(same(e[i].id, id, s)) { ++e[i].cnt; return e[i].cnt - 1; } e[++tot] = (List) {h[val], id, 1}, h[val] = tot; return 0; } } using Hash_List :: insert; using Hash_List :: ini; int one(int x) { int ret = 0; while(x) ++ret, x &= (x - 1); return ret; } int main() { scanf("%d%d", &n, &k); c[0][0] = 1; for(int i = 1; i <= 7; ++i) { c[i][0] = 1; for(int j = 1; j <= i; ++j) c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; } for(int i = 1; i <= n; ++i) for(int j = 0; j < 6; ++j) scanf("%d", &a[i][j]); for(int i = 0; i < 64; ++i) { int t = one(i); if(t < k) continue; Hash_List :: ini(); for(int j = 1; j <= n; ++j) ans += (((t - k) & 1) ? -1 : 1) * (ll)Hash_List :: insert(j, i) * (ll)c[t][k]; } printf("%lld\n", ans); return 0; }