cf103202M. United in Stormwind

题目描述

题解

假设选出题目的集合为 $S$ ,考虑求出它的数对数。

$(i,j)$ 如果不相同,则 $a_i ext{xor} a_j & S=0$ 。

因此我们先用 $ ext{fwt}$ 求出异或值为 $T$ 的数对数,然后对于 $S$ 来说,如果 $T$ 上的值能贡献答案,说明 $S &T>0$ 。

考虑容斥,减去 $S&T=0$ 的数对数即可。

也就是说, $T$ 的补集包含了 $S$ 。

因此考虑 $ ext{sosdp}$ , $f_{i,j}$ 表示 $x&i=x$ 并且 $x ext{xor} i<2^j$的 $x$ 的答案的和。

考虑转移,如果 $i$ 的第 $j$ 位为 $0$ ,那么 $f_{i,j}=f_{i,j-1}$ ,否则 $f_{i,j}=f_{i,j-1}+f_{i ext{xor} 2^j,j-1}$ 。

效率: $O(m2^m)$ 。

代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1<<20;
int n,m,a[N],t,ans;
LL Z,A[N<<1],B[N<<1],f[N];
char ch[25];
void Fwt(LL *g,int o){
    for (int i=1;i<t;i<<=1)
        for (int j=0;j<t;j+=(i<<1))
            for (int k=0;k<i;k++){
                LL x=g[j+k],y=g[i+j+k];
                g[j+k]=x+y;g[i+j+k]=x-y;
                if (!o) g[j+k]/=2,g[i+j+k]/=2;
            }
}
int main(){
    cin>>n>>m>>Z;
    for (int i=1;i<=n;i++){
        scanf("%s",ch);
        for (int j=0;j<m;j++)
            a[i]=(a[i]<<1)|(ch[j]=='A');
        A[a[i]]++;B[a[i]]++;
    }
    t=1<<m+1;Fwt(A,1);Fwt(B,1);
    for (int i=0;i<t;i++) A[i]*=B[i];
    Fwt(A,0);t>>=1;
    for (int i=0;i<t;i++) f[i]=A[i];
    for (int i=0;i<m;i++)
        for (int j=0;j<t;j++)
            if (j&(1<<i)) f[j]+=f[j^(1<<i)];
    for (int i=0;i<t;i++){
        LL v=1ll*n*n-f[(t-1)^i];
        if (v>=Z+Z) ans++;
    }
    cout<<ans<<endl;return 0;
}
原文地址:https://www.cnblogs.com/xjqxjq/p/15519174.html