• 【BZOJ4903/UOJ300】【CTSC2017】吉夫特


    Description

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

    Solution

      
      首先探究组合数的奇偶性问题。我们用Lucas定理展开组合数,可以发现一些有趣的性质:

    [{achoose b}={lfloorfrac a 2 floorchoose lfloor frac b2 floor}{amod2 choose bmod 2} ]

      后一个括号的值可以直接算:({0choose 0}={1choose 0}={1choose 1}=1,;;{0choose 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
    ",plu(ans,MOD));
    	return 0;
    }
    
  • 相关阅读:
    CSP2019滚粗记
    [总结] wqs二分学习笔记
    [总结] 圆方树学习笔记
    [CF960G] Bandit Blues
    [总结] 第一类斯特林数
    [EOJ629] 两开花
    [CF286E] Ladies' shop
    [总结] 动态DP学习笔记
    [BZOJ3879] SvT
    [总结] 替罪羊树学习笔记
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/9248627.html
Copyright © 2020-2023  润新知