zoukankan      html  css  js  c++  java
  • 【AtCoder】ARC092 D

    【题意】给定n个数的数组A和数组B,求所有A[i]+B[j]的异或和(1<=i,j<=n)。n<=200000。

    【算法】二分+模拟

    【题解】将答案分成(A[i]+B[j]-A[i]^B[j])的异或和 以及 A[i]^B[j]的异或和,即单独考虑进位(后面部分很好算)。

    二进制题目必须拆位,通过进位使第k位+1的数对必须满足 ( A[i] & ((1<<k)-1) ) + ( B[i] & ((1<<k)-1) ) >= (1<<k)

    首先预处理cx[k][i]=B[i] & ((1<<k)-1),然后对所有cx[k]排序。(如果是累加预处理的话先全部处理出来再排序)

    枚举数位k,然后枚举A[i] & ((1<<k)-1),在cx[k-1]中二分出满足要求的数对个数%2后累加进答案。

    最后异或和部分就很好算啦。

    复杂度O(n*28*log n)。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<set>
    #include<vector>
    #include<algorithm>
    #define ll long long
    #define lowbit(x) x&-x
    using namespace std;
    int read(){
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    int min(int a,int b){return a<b?a:b;}
    int max(int a,int b){return a<b?b:a;}
    int ab(int x){return x>0?x:-x;}
    //int MO(int x){return x>=MOD?x-MOD:x;}
    //void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
    /*------------------------------------------------------------*/
    const int inf=0x3f3f3f3f,maxn=200010;
    
    int n,A[maxn],B[maxn],cx[40][maxn],dx[maxn],ans=0;
    
    int main(){
        n=read();
        for(int i=1;i<=n;i++)A[i]=read();
        for(int i=1;i<=n;i++)B[i]=read();
        for(int k=0;k<28;k++){
            if(!k)for(int i=1;i<=n;i++)cx[k][i]=((1<<k)&B[i]);else
            for(int i=1;i<=n;i++)cx[k][i]=cx[k-1][i]^((1<<k)&B[i]);
        }
        for(int k=0;k<28;k++)sort(cx[k]+1,cx[k]+n+1);//
        for(int k=1;k<=28;k++){
            for(int i=1;i<=n;i++){
                dx[i]^=(1<<(k-1))&A[i];
                int x=lower_bound(cx[k-1]+1,cx[k-1]+n+1,((1<<k)-dx[i]))-cx[k-1];
                ans^=(((n-x+1)%2)<<k);
            }
        }
        int a,b,c,d;
        for(int k=0;k<=28;k++){
            a=b=c=d=0;
            for(int i=1;i<=n;i++)if((1<<k)&A[i])b++;else a++;
            for(int i=1;i<=n;i++)if((1<<k)&B[i])d++;else c++;
            ans^=(((1ll*a*d%2+1ll*b*c%2)%2)<<k);
        }
        printf("%d",ans);
        return 0;
    }
    

      

    n《=100000的俩序列,数字范围,问所有的个数字的异或和。
    
    这种东西肯定是按位考虑嘛,从低位开始然后补上进位。比如说第一位俩串分别有个和个,个和个,然后这一位就是个,会进个给第二位。但这里没法解决连续进位的问题,因为连续进位必须去具体考察哪几个数字进了位,复杂度不对。
    
    思维定势--考察某一位答案时从“模拟加法”“进位”的角度,无法从中跳出是不可能出解的。
    
    那换个方法呗,可以看到某一位的答案是由不高于这一位的所有位决定的。假如现在在第位,把和的所有数字取出最低的位,然后可以发现,或时,这一位会产生一个1。这俩式子移下项,发现只要把排序就可以枚举然后二分答案了。
    
    这个故事告诉我们,当发现自己陷入思维瓶颈的时候,一定要往后退一步。
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    //#include<queue>
    //#include<time.h>
    //#include<complex>
    #include<algorithm>
    #include<stdlib.h>
    using namespace std;
     
    int n;
    #define maxn 200011
    int a[maxn],b[maxn],na[maxn],nb[maxn];
    int main()
    {
        scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) scanf("%d",&b[i]);
        int ans=0;
        for (int i=0;i<=28;i++)
        {
            for (int j=1;j<=n;j++) na[j]=a[j]&((1<<(i+1))-1),nb[j]=b[j]&((1<<(i+1))-1);
            sort(nb+1,nb+1+n); int t1=1<<i,t2=2*t1,t3=3*t1,t4=4*t1;
            int now=0;
            for (int j=1;j<=n;j++)
            {
                now^=(lower_bound(nb+1,nb+1+n,t2-na[j])-lower_bound(nb+1,nb+1+n,t1-na[j]))&1;
                now^=(lower_bound(nb+1,nb+1+n,t4-na[j])-lower_bound(nb+1,nb+1+n,t3-na[j]))&1;
            }
            ans|=now<<i;
        }
        printf("%d
    ",ans);
        return 0;
    }
    

      

  • 相关阅读:
    yzoj P2344 斯卡布罗集市 题解
    yzoj P2350 逃离洞穴 题解
    yzoj P2349 取数 题解
    JXOI 2017 颜色 题解
    NOIP 2009 最优贸易 题解
    CH 4302 Interval GCD 题解
    CH4301 Can you answer on these queries III 题解
    Luogu2533[AHOI2012]信号塔
    Luogu3320[SDOI2015]寻宝游戏
    Luogu3187[HNOI2007]最小矩形覆盖
  • 原文地址:https://www.cnblogs.com/passion-sky/p/8594999.html
Copyright © 2011-2022 走看看