Codeforces Round #626 Div2 D. Present(位掩码,二分)

题目链接:https://codeforces.com/contest/1323/problem/D

题意:给了大小为4e5的数组a,其中1<=ai<=1e7。求所有点对和的异或和,即:



思路:

  • 按位来考虑,因为两个元素的和<=2e7,而2e7小于225,因此结果最多是25位。我们考虑答案中每一位ans[k]的值(0<=k<=24)。
  • 显然,ai中仅有0~k位会对第k位有影响,因此对数组a全部对2k+1取模,得到数组b,对b按升序排序。
  • 这样,0<=bi<=2k+1-1,两个bi的和<=2k+2-2。要使得和的第k位为1,和的范围应该为[ 2k , 2k+1-1 ] (区间1)或者 [ 2k+2k+1 , 2k+2-2] (区间2)。
  • 枚举bi,对两个区间可分别得到另一个和bi相加的元素bj的范围大小,利用二分查找bj的区间,从而得到bj的个数num。枚举完之后num%2=1的话,答案中的第k位便是1。
  • 另外,如果bi<=2k,那么bi和另一个元素的和只可能在区间1内,否则既可以在区间1,也可以在区间2。

AC code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=4e5+5;
typedef long long LL;
int n,a[maxn],b[maxn],p[30],ans,MOD;

void init(){
    p[0]=1;
    for(int i=1;i<=26;++i)
        p[i]=p[i-1]<<1;
}

int bs1(int ll,int rr,int v){
    int l=ll,r=rr,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(b[mid]>=v) r=mid-1;
        else l=mid+1;
    }
    return l;
}

int bs2(int ll,int rr,int v){
    int l=ll,r=rr,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(b[mid]<=v) l=mid+1;
        else r=mid-1;
    }
    return r;
}

int main(){
    init();
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    for(int k=0;k<=24;++k){
        LL num=0;
        MOD=p[k+1];
        for(int i=1;i<=n;++i)
            b[i]=a[i]%MOD;
        sort(b+1,b+n+1);
        for(int i=1;i<n;++i){
            int le=max(p[k]-b[i],0),ri=p[k+1]-1-b[i];
            int l=bs1(i+1,n,le);
            int r=bs2(i+1,n,ri);
            num+=(LL)(r-l+1);
            if(b[i]<=p[k]) continue;
            le=p[k+1]+p[k]-b[i],ri=p[k+2]-2-b[i];
            l=bs1(i+1,n,le);
            r=bs2(i+1,n,ri);
            num+=(LL)(r-l+1);
        }
        if((int)(num%2)==1) ans+=p[k];
    }
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/FrankChen831X/p/12442160.html

时间: 2024-08-28 02:32:09

Codeforces Round #626 Div2 D. Present(位掩码,二分)的相关文章

Codeforces Round #626 D. Present 异或按位确定 +二分or双指针

Codeforces Round #626 D. Present 异或按位确定 +二分or双指针 题意 给n个数,求他们两两的和的异或结果 n(4e5) 值域(1e7) 思路 异或问题一般都是按位确定,那么怎么确定第k位的值呢.首先第k位的值只和[1,k]位有关系,也就是说只跟a[i]本来在这一位有的数和进位有关系.那么怎么处理和的问题呢.首先我们只考虑[1..k]位,那么他们的和记为sum,如果sum在第k位有值 那么sum的值为 \(2^k+x\)因为只考虑[1..k]位所以a的最大值为\(

【前行】◇第3站◇ Codeforces Round #512 Div2

[第3站]Codeforces Round #512 Div2 第三题莫名卡半天……一堆细节没处理,改一个发现还有一个……然后就炸了,罚了一啪啦时间 Rating又掉了……但是没什么,比上一次好多了:) +传送门+ ◇ 简单总结 前两道题非常OK,秒掉还好.心态爆炸是从第三题开始的……一开始设计的判断方法没有考虑0,然后就错了很多次,然后改了过后又没有考虑整个数要分成2个及以上的数,继续WA,最后整个程序删了重新魔改了一次终于AC.感觉最后的AC代码的复杂度要比原来高一些,但是毕竟AC了,还是不

codeforces round #257 div2 C、D

本来应该认真做这场的,思路都是正确的. C题,是先该横切完或竖切完,无法满足刀数要求,再考虑横切+竖切(竖切+横切), 因为横切+竖切(或竖切+横切)会对切割的东西产生交叉份数,从而最小的部分不会尽可能的大. 代码如下,虽然比较长.比较乱,但完全可以压缩到几行,因为几乎是4小块重复的代码,自己也懒得压缩 注意一点,比如要判断最小块的时候,比如9行要分成2份,最小的剩下那份不是9取模2,而应该是4 m/(k+1)<=m-m/(k+1)*k          #include<bits/stdc+

codeforces Round #250 (div2)

a题,就不说了吧 b题,直接从大到小排序1-limit的所有数的lowbit,再从大到小贪心组成sum就行了 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #define N 200000 6 using namespace std; 7 int pos[N],a[N],s[N],f[N],la[N],b[N],i,j,k,ans,n,p

Codeforces Round#320 Div2 解题报告

Codeforces Round#320 Div2 先做个标题党,骗骗访问量,结束后再来写咯. codeforces 579A Raising Bacteria codeforces 579B Finding Team Member codeforces 579C A Problem about Polyline codeforces 579D "Or" Game codeforces 579E Weakness and Poorness codeforces 579F LCS Aga

Codeforces Round #254(div2)A

很有趣的题.想到了就非常简单,想不到就麻烦了. 其实就是一种逆向思维:最后结果肯定是这样子: WBWBWBWB... BWBWBWBW... WBWBWBWB... ... 里面有“-”的地方改成“-”就行了. 但是我开始是正着想的,想每个点怎么处理,这还要看它周围点的状态,越想越麻烦... 这题中体现的正难则反的逆向思维很值得学习. #include<iostream> #include<cstdio> #include<cstdlib> #include<cs

Codeforces Round #254(div2)B

就是看无向图有几个连通块,答案就是2n-num. 范围很小,就用矩阵来存图减少代码量. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<map> #include<set> #include<vector> #include<algorithm> #inc

Codeforces Round #260(div2)C(递推)

有明显的递推关系: f[i]表示i为数列中最大值时所求结果.num[i]表示数i在数列中出现了几次. 对于数i,要么删i,要么删i-1,只有这两种情况,且子问题还是一样的思路.那么很显然递推一下就行了:f[i]=max(f[i-1],f[i-2]+i*num[i]); 这里技巧在于:为了防止麻烦,干脆就所有数的出现次数都记录一下,然后直接从2推到100000(类似于下标排序),就不用排序了,也不用模拟删除操作了.这一技巧貌似简单,但实际上临场想出来也需要点水平. #include<iostrea

Codeforces Round #289 Div2 E

Problem 给一串长度为N的字符串,对于每个字符,若字符为元音,则权值为1,否则为0.一个子串的权值定义为该串所有字符权值之和除以字符个数,一个母串的权值定义为所有子串的权值之和.求母串的权值. Limits Time Limit(ms): 1000 Memory Limit(MB): 256 N: [1, 5*10^5] 字符集: 'A'-'Z' 元音: I E A O U Y Solution 考虑每个元音字符对母串的贡献,可以找出规律. More 举"ABCDOEFGHKMN"