zoukankan      html  css  js  c++  java
  • [51nod1684]子集价值

      lyk最近在研究位运算。
      它发现除了xor,or,and外还有很多运算。
      它新定义了一种运算符“#”。
      具体地,可以由4个参数来表示。
      ai,j表示
      i#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<...<ck,其中1≤c1且ck≤n ,这个序列的价值为 bc1 # bc2 #...# bck 的平方。
      这里我们假设k是正整数,因此满足条件的c的序列一定是 2n−1 。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
      一行表示答案。

      发现没法按位算贡献..然后就不会写了。

      看题解才发现自己tooyoung

    考虑答案的形式,假设一个子集经过题目中描述的位运算之后值为x,那么这个子集对答案的贡献为x^2。

    我们将x分解是二的幂次,若x&(1<<i)为0,则xi=0,否则xi=(1<<i)。
    那么我们将x^2分解后可以得到如下形式(x0+x1+...xp-1)^2。
    分解上式得到x0^2+x0x1+...+x0xk-1+x1x0+x1^2+...+x1xp-1+...+。
    我们发现这个式子中最多只与两个位有关。
    因此我们可以递推。
    枚举i和j,表示选择了b中的第i位和第j位,令dp[0/1][0/1]表示通过lyk所给的新的位运算后,到当前枚举的位置,第i位为0/1,第j位为0/1的方案总数。
    那么最后将2^(i+j)乘上dp[1][1]就是对答案的贡献了。
    只需将这些贡献累计起来就是答案了。
    总复杂度为p^2n。
     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<queue>
     6 #include<cmath>
     7 #include<cstdlib>
     8 #include<bitset>
     9 //#include<ctime>
    10 #define ll long long
    11 #define ull unsigned long long
    12 #define ui unsigned int
    13 #define d double
    14 //#define ld long double
    15 using namespace std;
    16 const int maxn=50233,modd=1000000007;
    17 int f[2][2][2],a[maxn];
    18 bool to[2][2];
    19 int i,j,k,n,m,p;
    20 
    21 int ra,fh;char rx;
    22 inline int read(){
    23     rx=getchar(),ra=0,fh=1;
    24     while((rx<'0'||rx>'9')&&rx!='-')rx=getchar();
    25     if(rx=='-')fh=-1,rx=getchar();
    26     while(rx>='0'&&rx<='9')ra=ra*10+rx-48,rx=getchar();return ra*fh;
    27 }
    28 
    29 inline void M(int &x){if(x>=modd)x-=modd;}
    30 inline int get(int p1,int p2){
    31     bool now=1,pre=0,n1,n2;
    32     f[pre][0][0]=/*1,*/f[pre][0][1]=f[pre][1][0]=f[pre][1][1]=0;
    33     for(register int i=1;i<=n;i++,swap(now,pre))
    34         n1=(a[i]>>p1)&1,n2=(a[i]>>p2)&1,
    35         memcpy(f[now],f[pre],4<<2),M(++f[now][n1][n2]),
    36         M(f[now][to[0][n1]][to[0][n2]]+=f[pre][0][0]),
    37         M(f[now][to[0][n1]][to[1][n2]]+=f[pre][0][1]),
    38         M(f[now][to[1][n1]][to[0][n2]]+=f[pre][1][0]),
    39         M(f[now][to[1][n1]][to[1][n2]]+=f[pre][1][1]);
    40     return f[pre][1][1];
    41 }
    42 int main(){
    43     n=read(),p=read();
    44     for(i=0;i<2;i++)for(j=0;j<2;j++)to[i][j]=read();
    45     for(i=1;i<=n;i++)a[i]=read();
    46     int ans=0;
    47     for(i=0;i<p;i++)for(j=0;j<p;j++)ans=(ans+ 1ll*(1ll<<(i+j))%modd*get(i,j))%modd;
    48     printf("%d
    ",ans);
    49 }
    View Code
  • 相关阅读:
    touch创建文件
    excel如何冻结首行或首列及首行首列同时冻结
    cd mkdir mv cp rm 命令目录相关操作
    months_between()用法
    sysdate()简单用法
    round()和trunc()用法
    length() 用法
    replace 用法
    orcl 中upper()和lower()和initcap()的用法
    orcl 复杂查询
  • 原文地址:https://www.cnblogs.com/czllgzmzl/p/5958176.html
Copyright © 2011-2022 走看看