问题描述
ZYB喜欢研究Xor,现在他得到了两个长度为n的数组A,B。于是他想知道:对于所有数对(i,j)(i∈[1,n],j∈[1,n]),(Ai+Bj)的xor之和为多少 定义多个数A1,A2...Ak的xor之和为A1xorA2xorA3xor...xorAk
输入描述
一共T(T≤10)组数据,对于每组数据: 第一行一个正整数n,表示数组长度 第二行n个非负整数,第i个整数为Ai 第三行n个非负整数,第i个整数为Bi n∈[1,105],Ai,Bi∈[0,260] 保证所有n的和小于等于2∗105
输出描述
每组数据输出一行Case #x: ans。x表示组数编号,从1开始。ans为所求值。
输入样例
1 5 8 11 30 28 0 28 27 22 0 0
输出样例
Case #1: 34
1003 ZYB loves Xor II 我们考虑两个数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<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string.h> #include<vector> #include<algorithm> #define ll __int64 #define maxn 110000 using namespace std; vector<ll>q[2]; ll x[maxn],y[maxn]; ll bit[100]; int n; ll solve()//我擦。。这个归并太吊了 { ll ans=0; for(ll i=0;i<62;i++) { ll tot=0; q[0].clear();q[1].clear(); for(ll j=1;j<=n;j++) q[(x[j]>>i)&1].push_back(x[j]); for(ll j=0;j<q[0].size();j++) x[++tot]=q[0][j]; for(ll j=0;j<q[1].size();j++) x[++tot]=q[1][j]; tot=0; q[0].clear();q[1].clear(); for(ll j=1;j<=n;j++) q[(y[j]>>i)&1].push_back(y[j]); for(ll j=0;j<q[0].size();j++) y[++tot]=q[0][j]; for(ll j=0;j<q[1].size();j++) y[++tot]=q[1][j]; ll len1=1,len2=1,len3=1; ll limit1=1ll<<i; ll limit2=1ll<<(i+1); ll all=(1ll<<(i+1))-1;///这里加1LL // cout<<all<<" all"<<endl; ll sum=0; // for(int i=1;i<=tot;i++) cout<<x[i]<<" "; // cout<<endl; // for(int i=1;i<=tot;i++) cout<<(x[i]&all)<<" "; // cout<<endl; // for(int i=1;i<=tot;i++) cout<<y[i]<<" "; // cout<<endl; for(ll i=n;i>=1;i--) { while((((x[i]&all)+(y[len1]&all))<limit1) && (len1<=n)) len1++;//这里多加几个() while((((x[i]&all)+(y[len2]&all))<limit2) && (len2<=n)) len2++; while(((x[i]&all)+(y[len3]&all)<limit1+limit2) && (len3<=n)) len3++;//cout<<(x[i]&all)<<" "<<(y[len3]&all)<<" "<<len3<<" "<<y[len3]<<" "<<(limit1+limit2)<<endl; sum+=n-(len1-1-len2+1+len3-1); // cout<<len1<<" "<<len2<<" "<<len3<<endl; // cout<<n-(len1-1-len2+1+len3-1)<<endl; } // cout<<sum<<" sum "<<i<<endl; if(sum&1) ans+=bit[i]; } return ans; } int main() { // freopen("1003.txt","r",stdin); int cas; scanf("%I64d",&cas); bit[0]=1; for(ll i=1;i<=61;i++) bit[i]=bit[i-1]*2; for(ll ca=1;ca<=cas;ca++) { scanf("%I64d",&n); for(ll i=1;i<=n;i++) scanf("%I64d",&x[i]); for(ll i=1;i<=n;i++) scanf("%I64d",&y[i]); // cout<<solve()<<endl; printf("Case #%I64d: %I64d\n",ca,solve()) ; } }