[BZOJ 3198] [Sdoi2013] spring 【容斥 + Hash】
题目链接:BZOJ - 3198
题目分析
题目要求求出有多少对泉有恰好 k 个值相等。
我们用容斥来做。
枚举 2^6 种状态,某一位是 1 表示这一位相同,那么假设 1 的个数为 x 。
答案就是 sigma((-1)^(x - k) * AnsNow * C(x, k)) 。注意 x 要大于等于 k。
对于一种状态,比如 10110,就是要保证第 1, 3, 4 个值相同。
这些值相同的对数怎么来求呢?使用Hash。
将这些位上的值 Hash 成一个数,然后枚举 [1, i] , 每次求出 [1, i-1] 有多少和 i 相同的,再把 i 加入 Hash 表。
代码
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; typedef long long LL; const int MaxN = 100000 + 5, Base = 11003, Mod = 10007; int n, k; int A[MaxN][10]; LL Ans, Temp; LL C[10][10]; void PrepareC() { C[0][0] = 1; for (int i = 1; i <= 6; ++i) { C[i][0] = 1; for (int j = 1; j <= i; ++j) C[i][j] = C[i - 1][j - 1] + C[i - 1][j]; } } struct HashNode { int B[10]; int Sum; HashNode *Next; } HA[MaxN], *P = HA, *Hash[Mod + 5]; inline bool Cmp(int *x, int *y) { for (int i = 0; i < 6; ++i) if (x[i] != y[i]) return false; return true; } int Get(int x, int k) { int Num[10]; LL HashNum = 0; for (int i = 0; i < 6; ++i) { HashNum = HashNum * Base % Mod; if (k & (1 << i)) { Num[i] = A[x][i]; HashNum += Num[i] % Base; HashNum %= Mod; } else Num[i] = 0; } HashNode *Now; Now = Hash[HashNum]; int ret = 0; while (Now) { if (Cmp(Now -> B, Num)) { ret = Now -> Sum; break; } Now = Now -> Next; } return ret; } void Add(int x, int k) { int Num[10]; LL HashNum = 0; for (int i = 0; i < 6; ++i) { HashNum = HashNum * Base % Mod; if (k & (1 << i)) { Num[i] = A[x][i]; HashNum += Num[i] % Base; HashNum %= Mod; } else Num[i] = 0; } HashNode *Now; Now = Hash[HashNum]; bool Flag = false; while (Now) { if (Cmp(Now -> B, Num)) { Flag = true; ++(Now -> Sum); break; } Now = Now -> Next; } if (Flag) return; ++P; P -> Sum = 1; for (int i = 0; i < 6; ++i) P -> B[i] = Num[i]; P -> Next = Hash[HashNum]; Hash[HashNum] = P; } int main() { scanf("%d%d", &n, &k); PrepareC(); for (int i = 1; i <= n; ++i) for (int j = 0; j < 6; ++j) scanf("%d", &A[i][j]); int Cnt; Ans = 0; for (int i = 0; i < 64; ++i) { Cnt = 0; for (int j = 0; j < 6; ++j) if (i & (1 << j)) ++Cnt; if (Cnt < k) continue; Temp = 0; memset(Hash, 0, sizeof(Hash)); P = HA; for (int j = 1; j <= n; ++j) { Temp += Get(j, i); Add(j, i); } if ((Cnt - k) & 1) Temp *= -1; Ans += Temp * C[Cnt][k]; } printf("%lld\n", Ans); return 0; }