P3773 [CTSC2017]吉夫特

传送门

看到组合数在模 $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}\binom{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\n",fk(ans-n+mo));
    return 0;
}

 

转载于:https://www.cnblogs.com/LLTYYC/p/11284584.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值