icpc2020沈阳 M (fwt+高维前缀和)
题目链接:https://codeforces.com/gym/103202/problem/M
一句话题意:求满足 $$\sum\limits_{i=1}^n \sum\limits_{j=i+1}^{n}[a_i \oplus a_j \cap S>0] >= k$$ 的集合 \(S\) 的数量
将问题分为两部分,第一步,求出 $$\sum\limits_{i=1}^{n} \sum\limits_{j=i+1}^{n} a_i \oplus a_j = S$$ 的 \((i,j)\)对的数量
令 \(num[i]\) 表示 \(i\) 的数量,答案即为 \(F[s] = \frac{1}{2}\sum\limits_{i\oplus j = S}num[i]*num[j]\),\(fwt\) 即可
第二步,如果 \(G[S] = \sum\limits_{T\cap S \neq 0} F[T] >= k\),则 \(S\) 有贡献,容斥一下变为 \(G[S] = \frac{n\times n}{2} - \sum\limits_{T\cap S = 0} F[T]\),即求 \(S\) 补集的子集和,高维前缀和可在 \(O(m2^m)\) 内求出
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200010;
const int maxm = (1 << 20) + 10;
int n, m, N; ll k;
ll a[maxm], b[maxm], g[maxn];
void fwt(ll *f,int type){
for(int i=1;i<N;i<<=1){
for(int j=0;j<N;j+=(i<<1)){
for(int k=0;k<i;k++){
ll p=f[j+k],q=f[i+j+k];
if(type==1) f[j+k]=p+q,f[i+j+k]=p-q;
else f[j+k]=(p+q)/2,f[i+j+k]=(p-q)/2;
}
}
}
}
char s[maxn];
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
scanf("%d%d%lld", &n, &m, &k);
N = (1 << m);
int num = 0;
for(int i = 1 ; i <= n ; ++i){
scanf("%s", s+1);
int len = strlen(s+1);
num = 0;
for(int j = 1 ; j <= len ; ++j){
num = (num<<1) + (s[j] == 'A' ? 1 : 0);
}
++a[num];
}
for(int i = 0 ; i < N ; ++i) b[i] = a[i];
fwt(a, 1), fwt(b, 1);
for(int i = 0 ; i < N ; ++i) a[i] = a[i] * b[i];
fwt(a, -1);
a[0] -= n;
for(int i = 0 ; i < N ; ++i) a[i] /= 2;
for(int j = 0 ; j < m ; ++j){
for(int i = 0 ; i < (1 << m) ; ++i){
if((i>>j) & 1) {
a[i] = a[i] + a[i^(1<<j)];
}
}
}
int ans = 0;
ll lim = 1ll*n*(n-1)/2-k;
int all = (1 << m) - 1;
for(int i = 0 ; i < N ; ++i){
if(a[all^i] <= lim) ++ans;
}
printf("%d\n", ans);
return 0;
}