Promblem A 小学组
给出一个位运算操作符$\oplus \in \{or , and , xor\}$ ,和$n$个$m$维向量$a_i$,其中$a_{i,j} \in \{0,1\}$。
并给出一个最终的目标$m$维向量$x$,求出长度为$k(1\leq k \leq n)$的不重复数组$p_i$的个数,
满足$1 \leq p_i \leq n$,使得$a_{p_1,i} \oplus a_{p_2 ,i } \oplus ...\oplus a_{p_k,i} = x_i$
对于$100\%$的数据,满足$1 \leq n,m \leq 25$
Solution :
考虑向量的维度时$m$,且比较小,而且是$0/1$,那么直接可以利用二进制状态压缩的办法将其存储。
每一次操作对于两个向量的两个相同维度做一次二进制操作,由于二进制的结合律,就相当于直接对整数做这些操作。
利用二进制运算的交换律可知,在一个确定的$p$的取值集合中交换任意两个数的顺序,不会对答案有任何影响。
那么这里,一旦一个$p$的集合合法,那么对于所有$p$集合的排列就必然合法。
利用这两个性质,我们直接就用$O(2^n)$枚举每一个向量取不取就好了,如果合法,就加上对应的排列数。
注意到,空集不是一个合法的方案。
# pragma GCC optimize(3) # include<bits/stdc++.h> # define ll long long # define Rint register int using namespace std; const int N=26; const int mo=1e9+9; char op; int n,m,base,t[N],a[N]; ll jc[N],ans; void dfs(Rint u,Rint cnt,Rint num) { if (u == n+1) { if (cnt == 0) return; if (num==base) (ans+=1ll*jc[cnt])%=mo; return; } t[u]=0; dfs(u+1,cnt,num); t[u]=1; if (op == '&') dfs(u+1,cnt+1,(num==0)?(a[u]):(num&a[u])); else if (op == '|') dfs(u+1,cnt+1,num|a[u]); else if (op == '^') dfs(u+1,cnt+1,num^a[u]); } signed main() { freopen("xx.in","r",stdin); freopen("xx.out","w",stdout); scanf("%c%d%d",&op,&n,&m); jc[0]=1;for (Rint i=1;i<=n;i++) jc[i]=jc[i-1]*i%mo; for (Rint i=1;i<=n;i++) { int num=0; for (Rint j=1;j<=m;j++) { int u; scanf("%d",&u); num=(num<<1)+u; } a[i] = num; } for (Rint i=1;i<=m;i++) { int u; scanf("%d",&u); base=(base<<1)+u; } dfs(1,0,0); printf("%lld\n",ans); return 0; }