题意:
有
n
n
n个试卷,每个试卷有
m
m
m个问题,每个问题有两个选项
a
,
b
a,b
a,b,定义两个试卷不同当且仅当其选中的问题中有一个问题不同。现在问你对于
m
m
m个问题的所有子集,有多少个子集问题不同的对数
≥
k
\ge k
≥k个。
1
≤
n
≤
2
e
5
,
1
≤
m
≤
20
,
1
≤
k
≤
n
∗
(
n
−
1
)
2
1\le n\le2e5,1\le m\le20,1\le k\le\frac{n*(n-1)}{2}
1≤n≤2e5,1≤m≤20,1≤k≤2n∗(n−1)
思路:
我们将不同选项看成
01
01
01串,两个集合不完全相同的话他们的异或肯定不为
0
0
0,对于我们已经选中的子集
S
S
S来说,他的答案为
F
(
S
)
=
∑
i
=
1
n
∑
j
=
i
+
1
n
[
a
n
s
i
⊕
a
n
s
j
>
0
]
F(S)=\sum_{i=1}^n\sum_{j=i+1}^n[ans_i\oplus ans_j>0]
F(S)=∑i=1n∑j=i+1n[ansi⊕ansj>0]。
考虑将这个式子转换一下,设
c
n
t
[
i
]
cnt[i]
cnt[i]代表
i
i
i这个二进制出现了几次,
F
(
S
)
=
1
2
∗
∑
i
⊕
j
=
S
c
n
t
[
i
]
∗
c
n
t
[
j
]
F(S)=\frac{1}{2}*\sum_{i\oplus j=S}cnt[i]*cnt[j]
F(S)=21∗∑i⊕j=Scnt[i]∗cnt[j],这个显然可以
f
w
t
fwt
fwt处理出来。
那么考虑
S
S
S对于哪些子集有贡献,举个例子,比如子集是
110
110
110,那么他有贡献的子集集合是
100
,
110
,
101
,
010
,
011
,
111
100,110,101,010,011,111
100,110,101,010,011,111,一开始没注意到
101
,
011
101,011
101,011这两个集合,以为直接
s
o
s
d
p
sosdp
sosdp求一遍超集一遍子集就有了,显然是不可以的。
正着来不好弄,我们考虑将其容斥一下。
对于
110
110
110,显然他没有贡献到的集合就是
001
001
001的超集,也就是其补集的超集,所以我们做一个容斥,记
G
[
S
]
G[S]
G[S]表示
S
S
S集合中对应答案相同的点对数量,这个可以用
s
o
s
d
p
sosdp
sosdp求出来超集,答案即为
∑
i
=
0
(
1
<
<
m
)
−
1
[
n
∗
(
n
−
1
)
2
−
G
[
i
]
>
=
k
]
\sum_{i=0}^{(1<<m)-1}[\frac{n*(n-1)}{2}-G[i]>=k]
∑i=0(1<<m)−1[2n∗(n−1)−G[i]>=k]。
// Problem: United in Stormwind
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/18713/M
// Memory Limit: 2097152 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;
//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=10000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,m;
LL a[N],k;
LL f[N],g[N];
char s[N];
void FWT_xor(LL *a,int opt,int N)
{
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
LL X=a[j+k],Y=a[i+j+k];
a[j+k]=(X+Y);a[i+j+k]=(X-Y);
if(opt==-1)a[j+k]=1ll*a[j+k]/2,a[i+j+k]=1ll*a[i+j+k]/2;
}
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++) {
scanf("%s",s);
int now=0;
for(int j=0;s[j];j++) {
if(s[j]=='B') now+=1<<j;
}
a[now]++;
}
FWT_xor(a,1,1<<m);
for(int i=0;i<1<<m;i++) a[i]=a[i]*a[i];
FWT_xor(a,-1,1<<m);
a[0]-=n;
for(int i=0;i<1<<m;i++) g[i^((1<<m)-1)]=a[i]/2;
for(int i=0;i<m;i++) {
for(int j=0;j<1<<m;j++) {
if(j>>i&1) g[j^(1<<i)]+=g[j];
}
}
int ans=0;
for(int i=1;i<1<<m;i++) {
LL now=1ll*n*(n-1)-g[i]*2;
if(now>=k*2) ans++;
}
cout<<ans<<endl;
return 0;
}
/*
110 -> 001
001 -> 110
111 -> 000
010
101
011
110 +1 -> 100 010 111 101 011 -> 100 110 101 111 | 010 110 011 111
001 +1
111 +1
100 +1 +1
010 +1 +1
001 +1 +1
110 +1
101 +1 +1 +1
011 +1 +1 +1
111 +1 +1 +1
*/