模拟题异或 (二进制)

Posted zager

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模拟题异或 (二进制)相关的知识,希望对你有一定的参考价值。

异或

问题描述

给定序列(??),计算(sum^{n}_{i=1}sum^{n}_{j-i}A_i and A_{i+1} and .... and A_j)

输入格式】

第一行一个整数(??).
第二行(??)个整数描述(??).

输出格式

一行输出答案(ans).

样例输入

3
1 2 3

样例输出

8

数据规模

对于(10\%)的数据,(?? ≤ 100, ??_i ≤ 1).
对于(20\%)的数据,(?? ≤ 100).
对于(30\%)的数据,(?? ≤ 1000)
对于(40\%)的数据,(?? ≤ 100000)
以上的数据档互不相交.
对于所有的数据,满足(1 ≤ ?? ≤ 10^5, 0 ≤ ??_i ≤ 2^{31 ? 1}).

题解

我们可以去利用二进制的(&)的性质,去计算二进制每一位的(1)的贡献。

因为(&)后有一位变成(0),那么它永远不会有贡献了(永远不会变成(1)),然后去计算会有多少个数会有这个(1)(权值为(1<<i))的贡献。

注意,一定要用(1LL),否则(2^{32})会炸。

code(&):

#include<iostream>
#include<cstdio>
#include<cctype>
#define N 100005
#define ll long long 
#define R register
using namespace std;
template<typename T>inline void read(T &a){
    char c=getchar();T x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    a=f*x;
}
int n,a[N],cnt;
ll ans;
int main(){
    read(n);
    for(R int i=1;i<=n;i++)read(a[i]);
    for(R ll i=0;i<=32;i++){
        cnt=0;
        for(R int j=1;j<=n;j++){
            if(a[j]&(1LL<<i))cnt++;
            else cnt=0;
            ans+=cnt*(1LL<<i);
        }
    }
    printf("%lld
",ans);
    return 0;
}

拓展

后来大佬GMPotlc又告诉了我一个处理异或(^)的做法。

都是利用二进制中的性质,异或偶数次无贡献,异或奇数次才有贡献。

而且若现在(1)的个数为奇数,那么就会与前面偶数次的产生贡献;当前为偶数,则反之。

所以我们记录一个出现(1)奇数次的个数(a)和偶数次的个数(b),总个数(cnt)

(cnt)为奇数 (ans+=(b*(1LL<<i)),a++;)

(cnt)为偶数 (ans+=(a*(1LL<<i)),b++;)

注意:初始化 ,(b=1)(因为一开始(0)(1)也是偶数)。

code(^):

#include<iostream>
#include<cstdio>
#include<cctype>
#define N 100005
#define ll long long 
#define R register
using namespace std;
template<typename T>inline void read(T &a){
    char c=getchar();T x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    a=f*x;
}
int n,init[N],cnt,a,b;
ll ans;
int main(){
    read(n);
    for(R int i=1;i<=n;i++)read(init[i]);
    for(R ll i=0;i<=32;i++){
        cnt=0;a=0;b=1;
        for(R int j=1;j<=n;j++){
            if(init[j]&(1LL<<i))cnt++;
            if(cnt&1)ans+=(1LL<<i)*b,a++;
            else ans+=(1LL<<i)*a,b++;
        }
    } 
    printf("%lld
",ans);
    return 0;
}

以上是关于模拟题异或 (二进制)的主要内容,如果未能解决你的问题,请参考以下文章

P6025 线段树(规律+模拟+位运算)

清北学堂模拟赛d6t4 数组异或

模拟测试20191023

LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段

模拟1486. 数组异或操作

17.11.04