• [BZOJ4903][Ctsc2017]吉夫特


    题目描述

    地址


    题解:

    因为奇*奇=奇,奇*偶=偶,偶*偶=偶,所以上面让求的其实就是找到一个序列,使得后面一个数和前面一个数的组合数是奇数。

    那么根据$Lucas$定理,$inom{n}{m} mod p=inom{n mod p}{m mod p} imes inom{frac{n}{p}}{frac{m}{p}}$。

    如果$p$是2,那么$inom{0}{0} = 1$, $inom{1}{0}=1$,$inom{0}{1} = 0$,$inom{1}{1}=1$,可知使得$inom{n}{m} mod 2 = 1$的必要条件是$n & m = m$.

    所以上面的条件可以转化为一个数列中后一项比前一项小,且后面那个数是前面的一个数的子集。

    那我们设$f[i]$表示以$i$结尾的满足题意的序列的方案个数,那么我们对于每个$a[i]$枚举它的子集$j$,如果它出现过并且位置在$i$之后那么$f[j]+=f[a[i]]$。

    这样同时保证了后面数的比前面的小并且后面的数是前面的数的子集。

    枚举子集的好处就是可以优化时间复杂度,因为如果暴力枚举的话是$O(N^2)$的,但是枚举子集是$O(3^{log233333})$的。

    这个优化方法很巧妙,如果以后做有关位运算方面的题的时候无法优化时间复杂度,可以试试用枚举子集。


    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define reg register
    inline int read() {
        int res = 0;char ch=getchar();bool fu=0;
        while(!isdigit(ch))fu|=(ch=='-'),ch=getchar();
        while(isdigit(ch)) res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
        return fu?-res:res;
    }
    
    int n;
    int a[2500000];
    int f[2500000], pos[2500000];
    int ans;
    #define mod 1000000007
    
    int main()
    {
        n = read();
        for (reg int i = 1 ; i <= n ; i ++) a[i] = read(), f[a[i]] = 1, pos[a[i]] = i;
        for (reg int i = 1 ; i <= n ; i ++)
            for (reg int j = a[i] ; j ; j = (j - 1) & a[i]) 
                if (a[i] != j and pos[j] > i) (f[j] += f[a[i]]) %= mod;
        for (reg int i = 1 ; i <= n ; i ++) (ans += f[a[i]] - 1) %= mod;
        cout << (ans + mod) % mod << endl;
        return 0;
    }
  • 相关阅读:
    bzoj 3035 二分答案+二分图最大匹配
    bzoj 1058 bst
    bzoj 1093 缩点+DP
    bzoj 1452 二维树状数组
    bzoj 1968 数学
    bzoj 1034 贪心
    牛客小白月赛12 I (tarjan求割边)
    Loj 103、10043 (KMP统计子串个数)
    poj 2356 (抽屉原理)
    hdu 1907 (尼姆博弈)
  • 原文地址:https://www.cnblogs.com/BriMon/p/9830606.html
Copyright © 2020-2023  润新知