AtCoder3943 Two Sequences (二分)

题意:

给长度为n的数组a和b
输出(a(1)+b(1))异或(a(1)+b(2))异或…(a(1)+b(n))异或(a(2)+b(1))异或…(a(2)+b(n))异或…(a(n)+b(n))的值
即从a中任意选择一个数a(i),b中任意选择一个数b(j),所有情况组成的a(i)+b(j)的异或和
数据范围:n<=2e5,0<=a(i),b(i)<=228

思路:

暴力显然超时。

看到异或很容易想到按位计算贡献
但是有加法很麻烦,因为加法可能会产生进位,怎么处理加法进位不是很好想。
假设我们当前要计算第x位的贡献,那么显然高于x的位对第x位是否为1不会产生影响,只考虑二进制的1到x位。
设T=2x,将a数组和b数组对2T取模,则得到低x位的值。
要使得a(i)+b(j)的第x位为1,则需满足以下两个条件之一:
T<=a(i)+b(j)<2T,(第x位不发生进位,此条件为第x位为1,x位下面任意)
3T<=a(i)+b(j)<4T,(第x位发生进位,此条件位第x和第x+1位都为1,x位下面任意)
因此可以枚举a(i),分别两次二分出满足这两种条件的区间端点,区间长度即为第x位为1的数对个数,
如果数对总和是奇数个则当前位对答案有贡献。

ps:
二分可以用lower_bound,码量会小很多(只是我不习惯用)。

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
int a[maxm];
int b[maxm];
int c[maxm];
int d[maxm];
int n;
int ask(int left,int right){//计算d数组中left<=x<right的x的数量
    int st=0,ed=0;
    int l=1,r=n;
    while(l<=r){//st尽量向左扩展
        int mid=(l+r)/2;
        if(d[mid]>=left){
            st=mid;
            r=mid-1;
        }else{
            l=mid+1;
        }
    }
    l=1,r=n;
    while(l<=r){//ed尽量向右扩展
        int mid=(l+r)/2;
        if(d[mid]<right){
            ed=mid;
            l=mid+1;
        }else{
            r=mid-1;
        }
    }
    if(!st||!ed)return 0;//如果不能同时满足x>=left且x<right,则无解
    return ed-st+1;
}
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];
    }
    int ans=0;
    for(int i=0;i<30;i++){
        int t=(1<<i);
        for(int j=1;j<=n;j++){
            c[j]=a[j]%(t*2);
            d[j]=b[j]%(t*2);
        }
        sort(c+1,c+1+n);
        sort(d+1,d+1+n);
        int cnt=0;
        for(int j=1;j<=n;j++){//枚举c
            cnt+=ask(t-c[j],t*2-c[j]);
            cnt+=ask(t*3-c[j],t*4-c[j]);
            cnt%=2;
        }
        if(cnt){
            ans+=(1<<i);
        }
    }
    cout<<ans<<endl;
    return 0;
}

发布了430 篇原创文章 · 获赞 36 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/104726529