【BZOJ4903/UOJ300】【CTSC2017】吉夫特

Description

  
  传送门
  
​  简述题意:给一个序列,询问有多少子序列满足其中不会出现\(a\choose b\)是偶数的情况,其中\(a\)\(b\)前面。
  
  
  

Solution

  
  首先探究组合数的奇偶性问题。我们用Lucas定理展开组合数,可以发现一些有趣的性质:
\[ {a\choose b}={\lfloor\frac a 2 \rfloor\choose \lfloor \frac b2\rfloor}{a\mod2 \choose b\mod 2} \]
  后一个括号的值可以直接算:\({0\choose 0}={1\choose 0}={1\choose 1}=1,\;\;{0\choose 1}=0\)。这相当于\(a\)\(b\)的二进制最末位的某种计算。
  
  而想象一下第一个括号递归计算的过程,实际上是移除了\(a\)\(b\)的二进制最后一位继续计算。到底层时,其值必定是1。
  
  所以决定总体奇偶的地方在于第二个括号会不会取0。也就是会不会出现\(a\)末位为0,\(b\)末位为1的情况。
  
  这整一个过程的实质是什么?相当于比较\(a\)\(b\)的每一位对应二进制。一旦出现\(a\)某一位为0,\(b\)对应位为1,则整体为偶数。否则整体为奇数。
  
  再进一步考虑,这种条件,相当于判断\(b\)的1位集合是否是\(a\)的1位集合的子集,则整体奇数,否则整体偶数。
  
  有趣的是,这种关系具有传递性:如果\(a\)包含\(b\),那么\(a\)包含以\(b\)开头的合法子序列的每个元素。问题变得非常简单,只需要考虑从哪一个子序列的开头转移:设\(f[a]\)表示以\(a\)为开头的子序列个数。枚举\(a\)的子集\(b\),如果\(b\)\(a\)后面,则\(f[a]+=f[b]\)
  
  总时间复杂度为\(\mathcal O(3^{\log_2n})\)
  
    
  

Code

  

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=211990,S=233335,MOD=1e9+7;
int n,a[N],p[S],f[S];
inline int plu(int x,int y){return (x+y)%MOD;}
inline void upd(int &x,int y){x=plu(x,y);}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    int ans=-n;
    for(int i=n;i>=1;i--){
        f[a[i]]=1;
        for(int j=(a[i]-1)&a[i];j;j=(j-1)&a[i])
            upd(f[a[i]],f[j]);
        upd(ans,f[a[i]]);
    }
    printf("%d\n",plu(ans,MOD));
    return 0;
}

转载于:https://www.cnblogs.com/RogerDTZ/p/9248627.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值