zoukankan      html  css  js  c++  java
  • 2019牛客多校第一场 H.XOR

    传送:https://ac.nowcoder.com/acm/contest/881/H

    题意:

    给定一个长度为$n$的数列,询问所有异或和为0的子集大小的和。

    数据范围:

    $1<=n<=10^5,1<=a_i<=10^{18}$。

    分析:

    首先先考虑暴力做法,需要枚举所有的子集情况。然后求异或和。(但这样肯定tle。

    考虑每一个数对于整个答案的贡献。

    已知:$X xor X=0$,所以如果一个数如果能被其他几个数表示,那么这个集合的异或和为0。

    首先,先求出$n$个数的线性基$LB1$。设线性基的秩为$r$,就是插入线性基内数的个数。

    1)考虑线性基外的数的贡献:线性基外的数有$n-r$个,对于一个$LB1$外的数$x$,有剩下的$n-r-1$个数可以与$x$组成$2^{n-r-1}$个子集。这些子集与线性基$LB1$都可构成异或和为0的集合。

    2)考虑线性基内的数的贡献:对于$LB1$内的数$x$,可以考虑用剩下的$n-1$个数构成的线性基$LB3$(秩为$r2$),然后判断$x$是否可以插入$LB3$内,如果不可以被插入,那么就是可以由$LB3$内的数异或构成,对于答案的贡献$2^{n-r2-1}$。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn=1e5+10;
     5 const ll mod=1e9+7;
     6 ll a[maxn]; vector<ll> kk; 
     7 struct Linear_basis{
     8     ll b[65]; int num;
     9     int insert(ll x){
    10         for (int i=62;i>=0;i--){
    11             if (x&(1ll<<i)){
    12                 if (!b[i]){
    13                     b[i]=x; num++; break;
    14                 } 
    15                 x^=b[i];
    16             }
    17         }
    18         return x>0;  //是否可插入 
    19         //true 可插入 
    20     }
    21     int checkin(ll x){
    22         for (int i=62;i>=0;i--){
    23             if (x&(1ll<<i)){
    24                 if (!b[i]) break;
    25                 x^=b[i];
    26             }
    27         }
    28         return x>0;  //是否可插入 
    29     }
    30     void clear(){
    31         memset(b,0,sizeof(b)); num=0;
    32     }
    33 }LB1,LB2,LB3;
    34 ll pow_(int x,int y){
    35     ll res=1ll,base=1ll*x;
    36     while (y){
    37         if (y&1) (res*=base)%=mod;
    38         (base*=base)%=mod;
    39         y>>=1;
    40     }
    41     return res;
    42 } 
    43 int main(){
    44     int n;
    45     while (~scanf("%d",&n)){
    46         LB1.clear();LB2.clear();LB3.clear(); kk.clear();
    47         for (int i=1;i<=n;i++){
    48             scanf("%lld",&a[i]);
    49             int f=LB1.insert(a[i]); 
    50             if (f) kk.push_back(a[i]);
    51             else LB2.insert(a[i]);
    52         }
    53         int r=LB1.num;
    54         if (n==r){  //所有数都被插入线性基 
    55             printf("0
    ");
    56             continue;
    57         }
    58         ll ans=1ll*(n-r)*pow_(2,n-r-1)%mod;
    59         for (int i=0;i<kk.size();i++){
    60             LB3=LB2;
    61             for (int j=0;j<kk.size();j++){
    62                 if (kk[i]==kk[j]) continue;
    63                 LB3.insert(kk[j]);
    64             }
    65             int r2=LB3.num;
    66             int f=LB3.checkin(kk[i]);
    67             if (!f) (ans+=pow_(2,n-r2-1))%=mod;
    68         }
    69         printf("%lld
    ",ans);
    70     }
    71     return 0;
    72 }
  • 相关阅读:
    2019-8-31-C#-控制台使用-UAC-权限
    2019-8-31-C#-控制台使用-UAC-权限
    2018-8-10-WPF-程序生成类库错误
    2018-8-10-WPF-程序生成类库错误
    2018-11-8-WPF-获取下载内容长度
    2018-11-8-WPF-获取下载内容长度
    2018-11-19-win10-uwp-使用-AppCenter-自动构建
    2018-11-19-win10-uwp-使用-AppCenter-自动构建
    Java实现 LeetCode 560 和为K的子数组(某著名排序大法改编)
    Java实现 LeetCode 560 和为K的子数组(某著名排序大法改编)
  • 原文地址:https://www.cnblogs.com/changer-qyz/p/11349296.html
Copyright © 2011-2022 走看看