cf103202M. United in Stormwind

题目描述
题解

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

( i , j ) (i,j) (i,j) 如果不相同,则 a i xor a j & S = 0 a_i \text{xor} a_j \& S=0 aixoraj&S=0

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

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

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

因此考虑 sosdp \text{sosdp} sosdp f i , j f_{i,j} fi,j 表示 x & i = x x\&i=x x&i=x 并且 x xor i < 2 j x \text{xor} i<2^j xxori<2j x x x 的答案的和。

考虑转移,如果 i i i 的第 j j j 位为 0 0 0 ,那么 f i , j = f i , j − 1 f_{i,j}=f_{i,j-1} fi,j=fi,j1 ,否则 f i , j = f i , j − 1 + f i xor 2 j , j − 1 f_{i,j}=f_{i,j-1}+f_{i \text{xor} 2^j,j-1} fi,j=fi,j1+fixor2j,j1

效率: O ( m 2 m ) O(m2^m) O(m2m)

代码
#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值