看到组合数在模 $2$ 意义下的乘积,考虑用 $lucas$ 定理把组合数拆开
$lucas$ 告诉我们,$C(n,m)$ 在模 $k$ 意义下的值,相当于 $n,m$ 在 $k$ 进制下每一位的组合数分别相乘的积在模 $k$ 意义下的值
就是若 $n=sum_{i=0}a[i]k^i$,$m=sum_{i=0}b[i]k^i$,其中 $a[i],b[i] in [0,k-1]$ ,那么 $C(n,m) equiv prod_{i=0}C(a[i],b[i]) (mod k)$
所以考虑把每一个组合数二进制拆分,变成一堆 $C(0,0)C(1,0)C(0,1)$ 相乘的形式
发现若要保证结果为奇数,则一定不能出现 $C(0,1)$ ,不然结果就等于 $0$
所以组合数为奇数当且仅当对于 $n$ 在二进制下的每一位,若为 $1$ 则 $m$ 在二进制下此位为 $0,1$ 都行,若为 $0$ 则 $m$ 在二进制下此为必须为 $0$
其实就是,若 $C(n,m)mod 2=1$ 当且仅当 $n & m=m$ ,其中 '&' 表示按位与
要求 $prod _{i=2}^{k}inom{a_{b_{i-1}}}{a_{b_i}} mod 2>0$ 其实就是对于每一个 $i in [2,k]$,都有 $a_{b_{i-1}} & a_{b_i}=a_{b_i}$
直接设 $f[i]$ 表示从后往前考虑到第 $i$ 位,满足要求的子序列数量
转移直接枚举子集暴力转移就好了,注意最后答案要减去子序列长度为 $1$ 的情况
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; typedef long double ldb; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e6+7,mo=1e9+7; inline int fk(int x) { return x>=mo ? x-mo : x; } int n,a[N],pos[N],f[N],ans; int main() { n=read(); for(int i=1;i<=n;i++) a[i]=read(),pos[a[i]]=i; for(int i=n;i;i--) { f[i]=1; for(int j=(a[i]-1)&a[i];j;j=(j-1)&a[i]) f[i]=fk(f[i]+f[pos[j]]); ans=fk(ans+f[i]); } printf("%d ",fk(ans-n+mo)); return 0; }