かっつのメモ帳

主に競プロ 時々日記

ARC092 D-Two Sequences

完全に忘れていたので復習

リンク

atcoder.jp

問題概要

長さNの数列A,Bが与えられる。それぞれの数列から要素を取り出してくる方法は N^2通り存在するが、その N^2通りの要素同士の和について全てXORを取った値を求めよ

制約

 1 \leqq N \leqq 2\times 10\  ^5

考察

当然愚直は間に合わないので桁毎に注目したい気分になります。

上の桁から見て行くのが頭の良いポイントです。

2進数で考えた時、K桁目に1がくる場合を考えます。

数列の全要素に対してmod 2^{K+1}を取ります。この操作が上の桁から見ることによって可能になります。

この時2要素の和の取りうる値Sは、 0 \leqq S \leqq 2^{K+2}-1となり、K桁目に1が来るのは 2^K \leqq S \leqq 2^{K+1}-1または2^K+2^{K+1} \leqq S \leqq 2^{K+2}-1の場合となります。数列Aの値に対してこの範囲に和が来るような数列Bの要素の個数は二分探索や尺取り法などによって求める事ができます。

実装

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int ll;

int main(){
	cin.tie(nullptr);
	ios::sync_with_stdio(false);
	int n; cin >> n;
	vector<int> a(n),b(n);
	for(int i=0;i<n;i++){
		cin >> a[i];
	}
	for(int i=0;i<n;i++){
		cin >> b[i];
	}
	int ans=0;
	for(int v=(1<<28);v;v>>=1){
		for(int i=0;i<n;i++){
			a[i]&=(v*2-1);
			b[i]&=(v*2-1);
		}
		sort(a.begin(),a.end());
		sort(b.begin(),b.end());
		int t=0;
		for(int i=n-1,j=0,k=0,l=0;i>=0;i--){
			while(j<n&&a[i]+b[j]<v)j++;
			while(k<n&&a[i]+b[k]<2*v)k++;
			while(l<n&&a[i]+b[l]<3*v)l++;
			t+=(k-j)+(n-l);
			t&=1;
		}
		if(t)ans|=v;
	}
	cout << ans << endl;
	return 0;
}

類題

Problem - B - Codeforces

View problem - XOR Sum (info1cup17_xorsum) :: oj.uz