Luogu P3298 [SDOI2013]泉

题目描述

https://www.luogu.com.cn/problem/P3298

Solution

我们考虑组合容斥

令 $f[k]=\sum_{i=k}^6\binom{i}{k}g_k$,我们发现这个东西比较容易计算

我们考虑枚举位置,然后求出这些位置相同的对数,这个东西就是 $f$,然后二项式反演求得 $g$ 即可

时间复杂度 $O(6\cdot 2^6 n+2^6n\log n)$,不用 $hash$ 好像怎么都过不了 = =

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
#define maxn 1000010
#define ll long long
using namespace std;

const int N = 6;

int n, k, a[maxn][N];

int C[10][10];
void init_C(int n) {
for (int i = 0; i <= n; ++i) C[i][0] = 1;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= i; ++j) C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}

const int base = 233333;
const ll p = 212370440130137957;
inline ll mul(ll x, ll y) {
return (x * y - (ll) ((long double) x / p * y) * p + p) % p;
}

ll Hash(vector<int> &a) {
ll s = 0;
for (auto u : a)
s = (mul(s, base) + u) % p;
return s;
}

ll f[maxn], g[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> k;
for (int i = 1; i <= n; ++i)
for (int j = 0; j < N; ++j) cin >> a[i][j];
int M = (1 << N) - 1;
for (int S = 0; S <= M; ++S) {
vector<ll> V;
int v = __builtin_popcount(S);
if (v < k) continue;
for (int i = 1; i <= n; ++i) {
vector<int> A;
for (int o = 0; o < N; ++o)
if (S >> o & 1) A.push_back(a[i][o]);
V.push_back(Hash(A));
} sort(V.begin(), V.end());
for (int i = 0, sz = V.size(), p = i; i < sz; i = p) {
while (p < sz && V[p] == V[i]) ++p;
f[v] += (ll) (p - i) * (p - i - 1) / 2;
}
} init_C(6);
for (int i = k, t = 1; i <= N; ++i, t = -t) g[k] += t * C[i][k] * f[i];
cout << g[k] << "\n";
return 0;
}