题面
题解
以下对于一个数 x x x,用 x i x_i xi 表示 x x x 在二进制下的第 i i i 位。如果这个数本身就带下标,如 a k a_k ak,那么用 a k , i a_{k,i} ak,i 表示 a k a_k ak 在二进制下的第 i i i 位。
发现带上绝对值很难搞,考虑如何确定绝对值的符号:
对于 ∣ a − b ∣ |a-b| ∣a−b∣ 来说,我们找到 a a a 和 b b b 在二进制下最高的不同位 y y y,那么如果 a y = 1 a_y=1 ay=1(那么 b y = 0 b_y=0 by=0),有 ∣ a − b ∣ = a − b |a-b|=a-b ∣a−b∣=a−b;如果 a y = 0 a_y=0 ay=0(那么 b y = 1 b_y=1 by=1),有 ∣ a − b ∣ = b − a |a-b|=b-a ∣a−b∣=b−a。
考虑从高到低枚举 x x x 二进制下的每一位,然后统计每一种情况的总和,最后答案就是总和的最小值。
枚举 x x x 过程中我们可以通过刚刚说的方法确定一些 ∣ a i − ( b i ⊕ x ) ∣ |a_i-(b_i\oplus x)| ∣ai−(bi⊕x)∣ 的绝对值的符号(下面会讲),显然初始时(枚举前)所有绝对值的符号都是不确定的。
那么设当前枚举到第 y y y 位,设当前 ∣ a i − ( b i ⊕ x ) ∣ |a_i-(b_i\oplus x)| ∣ai−(bi⊕x)∣ 绝对值符号还未确定的 i i i 的集合为 I I I。那么显然对于任意的 i ∈ I i\in I i∈I 和 j > y j>y j>y,都有 a i , j = ( b i ⊕ x ) j a_{i,j}=(b_i\oplus x)_j ai,j=(bi⊕x)j,不然绝对值符号就是可以确定的。那么对于 i ∈ I i\in I i∈I, ∣ a i − ( b i ⊕ x ) ∣ |a_i-(b_i\oplus x)| ∣ai−(bi⊕x)∣ 中比 y y y 更高的位对当前的总和都是没有贡献的。
不妨设当前位为第 y y y 位,按照 a i a_i ai 和 b i b_i bi 在当前位的情况可以把 I I I 分成两个集合:若 a i , y = b i , y a_{i,y}=b_{i,y} ai,y=bi,y 则把 i i i 分到 I 0 I_0 I0 集合;若 a i , y ≠ b i , y a_{i,y}\neq b_{i,y} ai,y=bi,y 则把 i i i 分到 I 1 I_1 I1 集合。
假设枚举 x x x 当前位为 0 0 0,那么 I 1 I_1 I1 部分的绝对值的符号是已经确定了的。
具体来说,对于 i ∈ I 1 i\in I_1 i∈I1:
- 若 a i , y = 1 a_{i,y}=1 ai,y=1(那么 b i , y = 0 b_{i,y}=0 bi,y=0),则 ( b i ⊕ x ) y = 0 (b_i\oplus x)_y=0 (bi⊕x)y=0,则 ∣ a i − ( b i ⊕ x ) ∣ = a i − ( b i ⊕ x ) |a_i-(b_i\oplus x)|=a_i-(b_i\oplus x) ∣ai−(bi⊕x)∣=ai−(bi⊕x)。那么此时 − ( b i ⊕ x ) -(b_i\oplus x) −(bi⊕x) 的第 y y y 位和 a i a_i ai 对于当前的总和的贡献已经可以确定了,为 2 y + ( a i and ( 2 y − 1 ) ) 2^y+(a_i \operatorname{and}\,(2^y-1)) 2y+(aiand(2y−1))。
- 若 a i , y = 0 a_{i,y}=0 ai,y=0(那么 b i , y = 1 b_{i,y}=1 bi,y=1),则 ( b i ⊕ x ) y = 0 (b_i\oplus x)_y=0 (bi⊕x)y=0,则 ∣ a i − ( b i ⊕ x ) ∣ = ( b i ⊕ x ) − a i |a_i-(b_i\oplus x)|=(b_i\oplus x)-a_i ∣ai−(bi⊕x)∣=(bi⊕x)−ai。那么此时 ( b i ⊕ x ) (b_i\oplus x) (bi⊕x) 的第 y y y 位和 − a i -a_i −ai 对于当前的总和的贡献已经可以确定了,为 2 y − ( a i and ( 2 y − 1 ) ) 2^y-(a_i \operatorname{and}\,(2^y-1)) 2y−(aiand(2y−1))。
那么之后我们就不用再考虑 a i a_i ai 对总和的贡献了。
而对于这些确定了绝对值符号的 b i b_i bi 在 0 ∼ y − 1 0\sim y-1 0∼y−1 位对总和的贡献,我们还需根据接下来枚举的 x x x 另行讨论。具体来说,我们记录一个数组 c n t ( j , v ) cnt(j,v) cnt(j,v) 表示:如果 x x x 第 j j j 位选了 v v v,那么这些已经确定了绝对值符号的 b i b_i bi 会对总和贡献 c n t ( j , v ) × 2 j cnt(j,v)\times 2^j cnt(j,v)×2j。
那么我们重新回到刚刚的那个讨论:(此时仍是假设枚举 x x x 当前位为 0 0 0,然后对于 i ∈ I 1 i\in I_1 i∈I1)
- 若 a i , y = 1 a_{i,y}=1 ai,y=1,则刚刚我们确定了 ∣ a i − ( b i ⊕ x ) ∣ = a i − ( b i ⊕ x ) |a_i-(b_i\oplus x)|=a_i-(b_i\oplus x) ∣ai−(bi⊕x)∣=ai−(bi⊕x)。那么此时对于所有的 0 ≤ j < y 0\leq j<y 0≤j<y,如果 x j x_j xj 取了 ( b i , j ⊕ 1 ) (b_{i,j}\oplus 1) (bi,j⊕1),那么 − ( b i ⊕ x ) -(b_i\oplus x) −(bi⊕x) 的第 j j j 位就会对总和贡献 − 2 j -2^j −2j,所以应该要让 c n t ( j , b i , j ⊕ 1 ) ← c n t ( j , b i , j ⊕ 1 ) − 1 cnt(j,b_{i,j}\oplus 1)\gets cnt(j,b_{i,j}\oplus 1)-1 cnt(j,bi,j⊕1)←cnt(j,bi,j⊕1)−1。
- 若 a i , y = 0 a_{i,y}=0 ai,y=0,则刚刚我们确定了 ∣ a i − ( b i ⊕ x ) ∣ = ( b i ⊕ x ) − a i |a_i-(b_i\oplus x)|=(b_i\oplus x)-a_i ∣ai−(bi⊕x)∣=(bi⊕x)−ai。那么此时对于所有的 0 ≤ j < y 0\leq j<y 0≤j<y,如果 x j x_j xj 取了 ( b i , j ⊕ 1 ) (b_{i,j}\oplus 1) (bi,j⊕1),那么 ( b i ⊕ x ) (b_i\oplus x) (bi⊕x) 的第 j j j 位就会对总和贡献 2 j 2^j 2j,所以应该要让 c n t ( j , b i , j ⊕ 1 ) ← c n t ( j , b i , j ⊕ 1 ) + 1 cnt(j,b_{i,j}\oplus 1)\gets cnt(j,b_{i,j}\oplus 1)+1 cnt(j,bi,j⊕1)←cnt(j,bi,j⊕1)+1。
那么我们可以进行这么一个递归函数: s o l v e ( I , y , c n t , c o s t ) solve(I,y,cnt,cost) solve(I,y,cnt,cost) 表示当前枚举到第 y y y 位、当前 ∣ a i − ( b i ⊕ x ) ∣ |a_i-(b_i\oplus x)| ∣ai−(bi⊕x)∣ 绝对值符号还未确定的 i i i 的集合为 I I I、在第 y y y 位以前记录的 c n t cnt cnt 数组、和在第 y y y 位以前记录的总和 c o s t cost cost。
那么对于当前枚举 x y = 0 x_y=0 xy=0 的情况,此时 I 1 I_1 I1 部分的绝对值符号就确定了,那么我们就更新 c n t cnt cnt 数组为 c n t ′ cnt' cnt′ 、更新 c o s t cost cost 为 c o s t ′ cost' cost′(记得加上 c n t ( y , 0 ) × 2 y cnt(y,0)\times 2^y cnt(y,0)×2y),并带下去递归 s o l v e ( I 0 , y − 1 , c n t ′ , c o s t ′ ) solve(I_0,y-1,cnt',cost') solve(I0,y−1,cnt′,cost′)。
对于当前枚举 x y = 1 x_y=1 xy=1 的情况也是类似的,此时 I 0 I_0 I0 部分的绝对值符号是已经确定了的。
边界条件是当前 I = ∅ I=\empty I=∅ 或 y < 0 y<0 y<0:
若 I = ∅ I=\empty I=∅,说明所有 i i i 的绝对值符号已经确定了,那么我们根据当前的 c n t cnt cnt 数组来计算真正的总和: s u m = c o s t + ∑ j = 0 y min ( c n t ( j , 0 ) , c n t ( j , 1 ) ) × 2 j sum=cost+\sum\limits_{j=0}^{y}\min(cnt(j,0),cnt(j,1))\times 2^j sum=cost+j=0∑ymin(cnt(j,0),cnt(j,1))×2j。而对于满足条件的 x x x 的个数,我们只需找出计算过程中有多少个 j j j 满足 c n t ( j , 0 ) = c n t ( j , 1 ) cnt(j,0)=cnt(j,1) cnt(j,0)=cnt(j,1)(这说明这一位既可以取 0 0 0 和取 1 1 1 的贡献是一样的),假设有 w w w 个,那么满足条件的 x x x 的个数为 2 w 2^w 2w。
若 y < 0 y<0 y<0,则真正的总和就是当前的 c o s t cost cost。
设 V V V 表示值域。注意到递归树上每一层中所有点的 I I I 集合大小的总和都不变,为 n n n,所以处理每一层的总时间都是 O ( n log V ) O(n\log V) O(nlogV) 的,故总时间复杂度为 O ( n log 2 V ) O(n\log^2 V) O(nlog2V)。
代码如下:
#include<bits/stdc++.h>
#define N 40010
#define int long long
#define ll long long
#define LNF 0x7fffffffffffffff
using namespace std;
inline ll read()
{
ll x=0;
int f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
struct data
{
ll v,num;
data(){};
data(ll a,ll b){v=a,num=b;}
void update(data b)
{
if(b.v<v) v=b.v,num=b.num;
else if(b.v==v) num+=b.num;
}
};
struct pi
{
ll a,b;
}p[N],I0[N],I1[N];
int n;
int cnt[50][2];
ll pow2[50];
inline bool get(ll x,int y)
{
return (x>>y)&1;
}
data solve(int y,int l,int r,ll precost)
{
if(l>r)
{
int w=0;
ll tmp=precost;
for(int i=0;i<=y;i++)
{
tmp+=min(cnt[i][0],cnt[i][1])*pow2[i];
if(cnt[i][0]==cnt[i][1]) w++;
}
return data(tmp,pow2[w]);
}
if(y<0) return data(precost,1);
int cnt0=0,cnt1=0;
for(int i=l;i<=r;i++)
{
if(get(p[i].a,y)==get(p[i].b,y)) I0[++cnt0]=p[i];
else I1[++cnt1]=p[i];
}
int mid=l+cnt0-1;
for(int i=1;i<=cnt0;i++) p[l+i-1]=I0[i];
for(int i=1;i<=cnt1;i++) p[mid+i]=I1[i];
data ans=data(LNF,114514);
int now[50][2];
if(1)//x=1
{
memcpy(now,cnt,sizeof(now));
ll tmp=precost;
if(l<=mid)
{
//I0
for(int i=l;i<=mid;i++)
{
if(get(p[i].a,y))
{
tmp+=pow2[y]+(p[i].a&(pow2[y]-1));
for(int j=0;j<y;j++)
cnt[j][get(p[i].b,j)^1]--;
}
else
{
tmp+=pow2[y]-(p[i].a&(pow2[y]-1));
for(int j=0;j<y;j++)
cnt[j][get(p[i].b,j)^1]++;
}
}
}
tmp+=cnt[y][1]*pow2[y];
ans.update(solve(y-1,mid+1,r,tmp));
memcpy(cnt,now,sizeof(cnt));
}
if(1)//x=0
{
memcpy(now,cnt,sizeof(now));
ll tmp=precost;
if(mid+1<=r)
{
//I1
for(int i=mid+1;i<=r;i++)
{
if(get(p[i].a,y))
{
tmp+=pow2[y]+(p[i].a&(pow2[y]-1));
for(int j=0;j<y;j++)
cnt[j][get(p[i].b,j)^1]--;
}
else
{
tmp+=pow2[y]-(p[i].a&(pow2[y]-1));
for(int j=0;j<y;j++)
cnt[j][get(p[i].b,j)^1]++;
}
}
}
tmp+=cnt[y][0]*pow2[y];
ans.update(solve(y-1,l,mid,tmp));
memcpy(cnt,now,sizeof(cnt));
}
return ans;
}
signed main()
{
n=read();
pow2[0]=1ll;
for(int i=1;i<=46;i++) pow2[i]=pow2[i-1]<<1ll;
for(int i=1;i<=n;i++) p[i].a=read();
for(int i=1;i<=n;i++) p[i].b=read();
data ans=solve(46,1,n,0);;
printf("%lld %lld\n",ans.v,ans.num);
return 0;
}
/*
5
3 1 5 2 4
4 2 1 5 4
*/
/*
3
3 3 2
2 0 2
*/
/*
2
5 8
6 5
*/