• [CTSC2017]吉夫特


    Description

    给出长为 (n) 的数列 ({a_n}),选出一个长度大于二的子序列,使得

    [prod_{i=2}^K inom{b_{i-1}}{b_i} mod 2=1 ]

    求方案数。

    Solution

    对组合数取模,容易想到卢卡斯定理,条件就转化为在二进制下,(b_{i-1}) 的每一位都要大于等于 (b_{i}),那么利用集合思想,就能推出 (b_{i-1}&b_i=b_i)。也就是说,满足这个条件就能在 (b_{i-1}) 后面接上一个 (b_i)。容易想到状态 (dp_{i}) 表示以 (a_i) 结尾的序列个数。转移显然。

    [dp_{i}=sum_{a_{j}&a_{i}=a_i\ quad j<i} dp_{j}+1 ]

    那就能得到一个 (O(n^2)) 的 dp,期望得分 70。

    再次观察,发现这题的值域很特殊,最大不过二十万。这就提示我们二进制分解下最多有 17 位。题目保证了 (a_i) 不同,那么一个 (a_i) 就唯一对应一个 (i),所以可以直接枚举 (a_i) 补集的子集再或上 (a_i),来枚举 (a_j) ,在判断一下 (j) 是不是在 (i) 前面即可。

    因为 (a_i) 不同,所以最坏复杂度就是

    [sum_{i=0}^{log a_i} inom{log a_i}{i} 2^i=(2+1)^{log a_i}=3^{log a_i} ]

    (O(3^{log a_i}))

    #include<stdio.h>
    #define Mod 1000000007
    #define N 240007
    
    inline int read(){
        int x=0,flag=1; char c=getchar();
        while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        return flag? x:-x;
    }
    
    int n,a[N],dp[N],pos[N];
    int main(){
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read(),pos[a[i]]=i;
        for(int i=n-1;i;i--){
            for(int j=a[i]&(a[i]-1);j;j=(j-1)&a[i])
                if(pos[j]>i) dp[i]=(dp[i]+dp[pos[j]]+1)%Mod;
        }
        int ans=0;
        for(int i=1;i<=n;i++) ans=(ans+dp[i])%Mod;
        printf("%d",ans);
    }
    
  • 相关阅读:
    python面向对象__call__
    python的上下文管理协议
    python析构方法__del__
    next和iter可迭代协议
    __slots__ 用法
    定制自己的数据类型
    内置的Attr系列
    反射和自省
    isinstance和issubclass
    BLE 学习
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14639500.html
Copyright © 2020-2023  润新知