原题链接
题意:给两个长度为n的序列Ai,Bi,求所有数(Ai + Bj)的异或和,i,j∈[0,n)。
被某大佬告知这是原题【hdu5270】,就去学习了下。
原始题解没找到,然后贴了份题解。
题解:
我们考虑两个数A,B。
为了描述方便,我们设[P]的值为:当表达式P的值为真时,[P]=1,否则[P]=0
我们现在考虑计算[(A+B)and(2i)>0]
首先我们将A,B都对2i+1取模,显然这样是不会影响答案的
则有一个十分显然的等式:
[(A+B)and(2i)>0]=[(A+B)≥(2i)]−[(A+B)≥(2i+1)]+[(A+B)≥(3∗2i)]
这个式子相当容易理解,这里不多述了
考虑每一位对答案的贡献是独立的,我们每一位分开做
于是现在问题变成了:给定数组A,B,求满足Ai+Bj≥limit的数对个数
我们可以将A,B排序后,直接O(n)计算即可
然而排序是O(nlogn)的,这样总复杂度就是O(nlognlogA)了,无法通过此题
于是这里有个小技巧
我们从高位往低位做,现在我们要实现的是:将A中每个数对P取模后将A排序
我们发现A会被分成两段,一段小于P,一段大于等于P,只有后面一段要取模,我们可以取模后直接将这两段归并,复杂度是O(n)的
时间复杂度:O(nlogA+nlogn)
参考:
#include <bits/stdc++.h>
using namespace std;
#define mem(a,n) memset(a,n,sizeof(a))
#define memc(a,b) memcpy(a,b,sizeof(b))
#define rep(i,a,n) for(int i=a;i<n;i++) ///[a,n)
#define pb push_back
#define mkp make_pair
#define mkt make_tuple
#define IO ios::sync_with_stdio(false)
#define fre freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double PI=acos(-1.0);
const double E=2.718281828459045;
const double eps=1e-8;
const int INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f3f3f3f3f;
const int MOD=1e3;
const int N=1e2+5;
const ll maxn=2e5+5;
const int dir[4][2]= {-1,0,1,0,0,-1,0,1};
ll a[maxn],b[maxn],n;
void Sort(ll *a, ll md)
{
int pos = n;
for (int i = 0; i < n; i ++)
{
if (pos == n && a[i] >= md)
pos = i;
a[i] = a[i] & (md - 1);
}
inplace_merge(a, a + pos, a + n);
}
bool solve(ll limit)
{
ll ans = 0;
int that = n - 1;
for (int i = 0; i < n; i ++)
{
while (that >= 0 && a[i] + b[that] >= limit)
that --;
ans += n - 1 - that;
}
return ans & 1;
}
inline ll Scan()
{
ll r = 0,f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-')f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') {r = r * 10 + ch - '0';ch = getchar();}
return r * f;
}
inline void Print(ll x)
{
if(x<0){x=-x;putchar('-');}
if(x>9) Print(x/10);
putchar(x%10+'0');
}
int main()
{
n=Scan();
rep(i,0,n) a[i]=Scan();
rep(i,0,n) b[i]=Scan();
sort(a,a+n);
sort(b,b+n);
ll ans=0;
for(int i=61; i>=0; i--)
{
Sort(a, 2LL<<i);
Sort(b, 2LL<<i);
ll res=solve(1LL<<i)^solve(2LL<<i) ^solve(3LL<<i);
ans|=res<<i;
}
Print(ans);
return 0;
}
另写下一份,容易想到的是二进制异或,拆分成位。
通过进位使第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后累加进答案。