zoukankan      html  css  js  c++  java
  • 51Nod 1684 子集价值 (平方和去括号技巧)

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1684

    题意:

    新建一个位运算,求所有子集通过这个位运算后的答案的平方和是多少。

     

    先想弱化版:

    新建一个位运算,求所有子集通过这个位运算后的答案的和是多少。

    枚举每一个二进制位,看有多少个子集能够使这一位为1

    dp[i]表示前i个数中,能使枚举的这一位为1的方案数

    根据第i个数选或者是不选转移

    ans= Σ  2^j * 第j位的dp[n] 

    这里是平方和

    设一个子集位运算后的结果为x,它对答案的贡献为x^2

    把x按二进制拆为p位,即(x0+x1+x2+x_p-1)

    其中xi表示2^i

    那它对答案的贡献为 (x0+x1+x2+x_p-1)^ 2

    去括号就是  x0*x0+x0*x1+……+x0*x_p-1+……+ x_p-1 * x0+x_p-1 * x1+…… x_p-1 * x_p-1

    即 Σ Σ xi*xj    i,j ∈[0,p)

     每一项至于两位有关

    所以枚举任意两位a,b

    dp[i][0/1][0/1]表示前i个数,第a位为0/1,第b位为0/1的方案数

    ans= Σ Σ 2^(i+j) * 枚举的两位为i和j时的dp[n][1][1]

    即dp求的是表达式中每一项的系数

    #include<cstdio>
    #include<cstring> 
    #include<iostream>
    using namespace std;
    
    #define N 50001
    
    const int mod=1e9+7;
    
    int n,p; 
    int to[2][2];
    int b[N];
    
    int dp[N][2][2];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }     
    
    int get(int p,int q)
    {
        memset(dp,0,sizeof(dp));
        bool pp,qq;
        for(int i=1;i<=n;++i)
        {
            pp=b[i]>>p&1;
            qq=b[i]>>q&1;
            dp[i][pp][qq]++;
            dp[i][pp][qq]-=dp[i][pp][qq]>=mod ? mod : 0;
            for(int j=0;j<2;++j)
                for(int k=0;k<2;++k)
                {
                    dp[i][j][k]+=dp[i-1][j][k];
                    dp[i][j][k]-=dp[i][j][k]>=mod ? mod : 0;
                    dp[i][to[j][pp]][to[k][qq]]+=dp[i-1][j][k]; 
                    dp[i][to[j][pp]][to[k][qq]]-=dp[i][to[j][pp]][to[k][qq]]>=mod ? mod : 0;
                }
        }
        return dp[n][1][1];
    }
    
    int main()
    {
        read(n); read(p);
        for(int i=0;i<2;++i)
            for(int j=0;j<2;++j)
                 read(to[i][j]);
        for(int i=1;i<=n;++i) read(b[i]);
        int ans=0;
        for(int i=0;i<p;++i)
            for(int j=0;j<p;++j)
            {
                ans+=(1LL<<i+j)%mod*(long long)get(i,j)%mod;
                ans-=ans>=mod ? mod : 0;
            }
        cout<<ans;
    } 
    基准时间限制:5 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
     收藏
     关注

    lyk最近在研究位运算。

    它发现除了xor,or,and外还有很多运算。

    它新定义了一种运算符“#”。

    具体地,可以由4个参数来表示。

     ai,j

    其中i,j与a的值均∈[0,1]。

    当然问题可以扩展为>1的情况,具体地,可以将两个数分解为p位,然后对于每一位执行上述的位运算,再将这个二进制串转化为十进制就可以了。

    例如当 a0,0=a1,1=0,a0,1=a1,0=1,3#4在p=3时等于7,2#3在p=4时等于1(实际上就是异或运算)。

    现在lyk想知道的是,已知一个数列b。

    它任意选取一个序列c,满足 c1<c2<...<ck1c1ckn ,这个序列的价值为 bc1 # bc2 #...# bck 的平方。

    这里我们假设k是正整数,因此满足条件的c的序列一定是 2n1 。lyk想知道所有满足条件的序列的价值总和是多少。

    例如样例中,7个子集的价值分别为1,1,4,4,9,9,0。总和为28。

    由于答案可能很大,只需对1,000,000,007取模即可。

    Input
    第一行两个整数n(1<=n<=50000),p(1<=p<=30)。
    第二行4个数表示a0,0,a0,1,a1,0,a1,1。(这4个数都∈{0,1})
    第三行n个数bi(0<=bi<2^p)。
    Output
    一行表示答案。
    Input示例
    3 30
    0 1 1 0
    1 2 3
    Output示例
    28
  • 相关阅读:
    vagrant 命令+配置+入门案例
    博客园皮肤 Cnblogs-Theme-SimpleMemory
    Vue:前后端交互、路由
    Vue :模块化
    Vue:组件开发
    Vue :模板语法
    ssm实战(11)-----用户功能开发
    ssm实战(10)-----通用功能开发(Windows环境下配置)
    ssm实战(9)-----前端开发(Windows环境下配置)
    spring boot实战——微信点餐系统03:微信授权(用户授权),免费内网穿透(固定ip)
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8454505.html
Copyright © 2011-2022 走看看