HGOI 20191029pm 题解

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;
}
xx.cpp

猜你喜欢

转载自www.cnblogs.com/ljc20020730/p/11761057.html
今日推荐