杭电多校2019-5B-Three arrays (01字典树解决异或问题)

本文介绍了一道名为Threearrays的问题,涉及两个整数数组a和b,目标是通过重新排列这两个数组的元素,找到一个字典序最小的c数组,其中c[i]=a[i]^b[i]。解决方案利用了01字典树来解决区间内与某个数异或最大/小问题。

杭电多校2019-5B-Three arrays (01字典树解决异或问题)

There are three integer arrays a,b,c. The lengths of them are all N. You are given the full contents of a and b. And the elements in c is produced by following equation: c[i]=a[i] XOR b[i] where XOR is the bitwise exclusive or operation.

Now you can rearrange the order of elements in arrays a and b independently before generating the array c. We ask you to produce the lexicographically smallest possible array c after reordering a and b. Please output the resulting array c.
Input
The first line contains an integer T indicating there are T tests.

Each test consists of three lines. The first line contains one positive integer N denoting the length of arrays a,b,c. The second line describes the array a. The third line describes the array b.

  • T≤1000

  • 1≤N≤105

  • integers in arrays a and b are in the range of [0,230).

  • at most 6 tests with N>100
    Output
    For each test, output a line containing N integers, representing the lexicographically smallest resulting array c.
    Sample Input
    1
    3
    3 2 1
    4 5 6

题目大意:

给定两个数组 a和b,a,b数组里面元素的顺序是可以随意变化的。
c[i] = a[i]^b[i]

求一个字典序最小的c数组并输出

解题思路:

看到异或 就可以想到用01字典树
01字典树能解决区间内与某个数异或最大/小问题

建立两棵01字典树,分别插入a数组中的每个数,和b数组中的每个数。
然后开始遍历两棵字典树,并记录每个节点走过的次数,用num数组记录。
因为是01字典树 只是2叉的。
先让字典树都向相同的方向走。00 11 为什么这样呢,因为这样的异或是0 如果不能走相同的方向再走不同的方向,同时让ans的这个位变为1,因为这个位的异或值是1了。最后返回一个答案给c数组,最后把c数组排个序就是我们要的答案。

几个问题:
1 如何让ans的这个位变为1 ?
我们引进一个ss数组,ss[i]就是 第i位是1,其他位都是0.
这样我们走第i位,如果想让i位变成1 那就加上ss[i]即可。

for(int i=0;i<=30;i++){
		ss[i] = 1<<i;
	}

2 如何遍历字典树
因为是2叉,我们可以枚举出每棵树的左右子节点,看有没有。

  int s10=tree1[p1][0],s11=tree1[p1][1];
  int s20=tree2[p2][0],s21=tree2[p2][1];

3。咋解决超时问题
我写的时候,不停地T,一直找不出为啥,后来知道了
在初始化数组的时候,我们要初始化tree和num数组 不必全部初始化,只需要初始化被改变的前max(tot1,tot2)个就可以。

void init(){

	int MAX=max(tot1,tot2);
	for(int i=0;i<=MAX;i++){
		tree1[i][0]=tree1[i][1]=0;
		tree2[i][0]=tree2[i][1]=0;;
		num[i][0]=0;
		num[i][1]=0;
	}
	tot1=tot2=0;
	cnt = 0;
}

(ps:膜拜的比我小一级的大佬的思路 我是真滴菜)

AC代码:

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const int maxn = 1e5+10;
int tree1[maxn*40][2];
int tree2[maxn*40][2];
int tot1=0,tot2=0;
int num[maxn*40][2];//每个结点的次数
int a[maxn],b[maxn];
int n;
int c[maxn];
inline void add(int x,int f){
	int root=0;
	int id;
	if(f==1){
		for(int i=30;i>=0;i--){
			id = (x>>i)&1;
			if(!tree1[root][id]) tree1[root][id] = ++tot1;
			root = tree1[root][id];
			num[root][0]++;
		}
	}else if(f==2){
		for(int i=30;i>=0;i--){
			id = (x>>i)&1;
			if(!tree2[root][id]) tree2[root][id] = ++tot2;
			root = tree2[root][id];
			num[root][1]++;
		}
	}
}

int ss[40];
int cnt = 0;
void init(){

	int MAX=max(tot1,tot2);
	for(int i=0;i<=MAX;i++){
		tree1[i][0]=tree1[i][1]=0;
		tree2[i][0]=tree2[i][1]=0;;
		num[i][0]=0;
		num[i][1]=0;
	}
	tot1=tot2=0;
	cnt = 0;
}

bool solve(){
    int p1=0;
	int p2=0;
    int ans=0;
    int i=0;
    for( i=0;i<=30;i++){
        int s10=tree1[p1][0],s11=tree1[p1][1];
        int s20=tree2[p2][0],s21=tree2[p2][1];
        if(s10&&s20){
            if(num[s10][0]>0&&num[s20][1]>0){
                num[s10][0]--;
                num[s20][1]--;
                p1=s10,p2=s20;
                continue;
            }
        }
        if(s11&&s21){
            if(num[s11][0]>0&&num[s21][1]){
                num[s11][0]--;
                num[s21][1]--;
                p1=s11,p2=s21;
                continue;
            }
        }
        if(s11&&s20){
            if(num[s11][0]>0&&num[s20][1]>0){
                num[s11][0]--;
                num[s20][1]--;
                p1=s11,p2=s20;
                ans+=ss[30-i];
                continue;
            }
        }
        if(s10&&s21){
            if(num[s10][0]>0&&num[s21][1]>0){
                num[s10][0]--;
                num[s21][1]--;
                p1=s10,p2=s21;
                ans+=ss[30-i];
                continue;
            }
        }
        break;

    }
    if(i<=30) return false;
    c[++cnt]=ans;
    return true;
}
int main(){
	std::ios::sync_with_stdio(0);
	for(int i=0;i<=30;i++){
		ss[i] = 1<<i;
	}
	int t;
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			add(a[i],1);
		}
		for(int i=1;i<=n;i++){
			cin>>b[i];
			add(b[i],2);
		}
	
		while(solve()){
			continue;	
		}
		sort(c+1,c+1+cnt);
		for(int i=1;i<=cnt;i++){
			cout<<c[i];
			if(i==cnt) cout<<endl;
			else cout<<" ";
		}
		init();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值