HDU 6625 three arrays 杭电多校第五场 B 良心题解

HDU 6625 three arrays

题意

给出a数组和b数组
c数组为 a [ i ] ⊕ b [ i ] a[i] \oplus b[i] a[i]b[i]
重排列 a , b a,b a,b数组使得得到的c数组字典序最小

思路

最小的为a[i]与b[j]相互对应

首先,从b数组中能找到 b [ j ] b[j] b[j] a [ i ] ⊕ a[i] \oplus a[i]最小,我们称 b [ j ] b[j] b[j] a [ i ] a[i] a[i]的对应点
如何查找对应点,我们可以用字典树(Trie树),将每个数字看成30位01字符串
我们将a[i]与b[i]与其对应点相连
在这里插入图片描述
显然,若a[i]与b[j]对应,并且b[j]与a[i]对应,那么a[i]与b[j]组合一定是当前可选的最小的
证明:显然,若存在 a [ i ] ⊕ b [ z ] &lt; a [ i ] ⊕ b [ j ] a[i]\oplus b[z]&lt;a[i]\oplus b[j] a[i]b[z]<a[i]b[j], a [ i ] a[i] a[i] b [ z ] b[z] b[z]对应,矛盾

必定存在 a [ i ] a[i] a[i] b [ j ] b[j] b[j]相互对应

一个显然的事情是 n n n个点和 n n n个点两两连线,必定存在回路
证明:不太清楚的童鞋可以尝试构造一下无回路的情况,会发现最后一个点必定会形成回路

不会存在路径超过2的回路

显然路径为2的回路存在是合理的,即为相互对应
对于路径超过2的路径
如下图,若存在a[x]对应b[z],b[z]对应a[y],连接a[x]与b[z]的异或值大于b[z]与a[y]的异或值
a [ 2 ] ⊕ b [ 1 ] &lt; a [ 2 ] ⊕ b [ 3 ] a[2]\oplus b[1]&lt;a[2] \oplus b[3] a[2]b[1]<a[2]b[3]在这里插入图片描述
下面我们简称一条边的值为一条边相连的两个值的异或
以下图为例,若存在回路,a[x]b[z]边大于a[y]b[z] ⋯ ⋯ \cdots\cdots ,必定再次回到a[x]b[z]边造成矛盾
a [ 2 ] b [ 1 ] &gt; a [ 2 ] b [ 3 ] &gt; a [ 3 ] b [ 3 ] &gt; a [ 3 ] b [ 1 ] &gt; a [ 2 ] b [ 1 ] a[2]b[1]&gt;a[2]b[3]&gt;a[3]b[3]&gt;a[3]b[1]&gt;a[2]b[1] a[2]b[1]>a[2]b[3]>a[3]b[3]>a[3]b[1]>a[2]b[1],矛盾!在这里插入图片描述

具体实现

  • 若栈区为空,随便加一个点入栈
  • 若栈顶的对应点不在栈中,将其入栈
  • 若栈顶的对应点在栈中,必定为栈定第二个(最爱top元素的元素)
    我们以下图来模拟以下该过程
    1、将a[1]入栈(随便加一个)
    栈区: a [ 1 ] a[1] a[1]
    2、将b[2]入栈(将栈顶元素最爱的元素入栈)
    栈区: a [ 1 ] , b [ 2 ] a[1],b[2] a[1],b[2]
    3、将a[3]入栈(将栈顶元素最爱的元素入栈)
    栈区: a [ 1 ] , b [ 2 ] , a [ 3 ] a[1],b[2],a[3] a[1],b[2],a[3]
    3、将b[3]入栈(将栈顶元素最爱的元素入栈)
    栈区: a [ 1 ] , b [ 2 ] , a [ 3 ] , b [ 3 ] a[1],b[2],a[3],b[3] a[1],b[2],a[3],b[3]
    4、将a[3]和b[3]出栈(发现 栈顶元素 最爱的元素 即为 最爱栈顶元素 的元素,相亲相爱,牵手成功)
    栈区: a [ 1 ] , b [ 2 ] a[1],b[2] a[1],b[2]
    在这里插入图片描述
    5、将a[2]入栈(b[2]移情别恋,爱上a[2])
    栈区: a [ 1 ] , b [ 2 ] , a [ 2 ] a[1],b[2],a[2] a[1],b[2],a[2]
    6、将b[1]入栈(将栈顶元素最爱的元素入栈)
    栈区: a [ 1 ] , b [ 2 ] , a [ 2 ] , b [ 1 ] a[1],b[2],a[2],b[1] a[1],b[2],a[2],b[1]
    7、将a[2]和b[1]出栈(发现 栈顶元素 最爱的元素 即为 最爱栈顶元素 的元素,相亲相爱,牵手成功)
    栈区: a [ 1 ] , b [ 2 ] a[1],b[2] a[1],b[2]在这里插入图片描述
    8、将a[1]和b[2]出栈(最爱你的人永远在你后面等你,单相思也会有结果qwq)
    栈区:空
    在这里插入图片描述
pii stack[maxn * 2];//first存大小,second存在哪个数组中,0表示a数组,1表示b数组
int top = 0;
int sum = 2 * n;
	while (sum) {
		if (!top) {			//栈区为空,随便从a数组中找一个数塞进去,估且就找个最爱1的吧
			stack[++top] = pii(T[0].find(1), 0);
			continue;
		}
		int symbol = T[stack[top].second ^ 1].find(stack[top].first);//symbol表示栈顶元素最爱元素
		if (top == 1 || stack[top - 1].first != symbol)		//栈顶元素最爱元素不在栈中
			stack[top + 1] = pii(symbol, stack[top].second ^ 1),	//入栈
			top++;
		else {
			res.push_back(stack[top].first ^ stack[top - 1].first);		//牵手成功双双出栈
			T[stack[top].second].insert(stack[top].first, -1);
			T[stack[top - 1].second].insert(stack[top - 1].first, -1);
			sum -= 2, top -= 2;
		}
	}

Trie树找最爱

插入与删除

一切尽在不言注释中

void insert(int str, int val) {				//val=1,表示插入一个数,val=-1表示删除一个数
		int position = root;				//初始化位置
		for (int i = 29; i >= 0; i--) {		//将每一位从高到低插入
			int symbol = (str >> i) & 1;	//提取二进制位
			if (!tree[position].son[symbol])//创建新节点
				tree[position].son[symbol] = ++num;
			position = tree[position].son[symbol];
			tree[position].mark += val;		//每一位都要记录有无,便于下方的查找
		}
	}
查找
int find(int str) {
		int position = root, w = 0;
		for (int i = 29; i >= 0; i--) {		//按二进制位从高到底查找
			int symbol = (str >> i) & 1;
			if (!tree[position].son[symbol] || !tree[tree[position].son[symbol]].mark) 
					symbol ^= 1;	//若该位symbol不存在,或者已被用光,那么选择另一个,0^1=1,1^1=0
			position = tree[position].son[symbol];			//迭代寻找
			w |= (symbol<< i);			//将该位存入答案
		}
		return w;
	}

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
#pragma warning (disable:4996)
typedef pair<int, int> pii;
const int maxn = 101000;
struct Tree {
	Tree() {
		son[0] = son[1] = mark = 0;
	}
	int mark;		//标记
	int son[2];	//此处只考虑小写字母
};
struct Trie {
	int root, num;		//根节点永久为0
	Tree tree[maxn * 33];
	Trie() {
		root = num = 0;
		memset(tree, 0, sizeof(0));
	}
	void init() {
		Tree st; 
		fill(tree, tree + num + 1, st);
		root = num = 0;
	}
	void insert(int str, int val) {
		int position = root;				//初始化位置
		for (int i = 29; i >= 0; i--) {
			int symbol = (str >> i) & 1;	//转化函数,视情况而定
			if (!tree[position].son[symbol])//创建新节点
				tree[position].son[symbol] = ++num;
			position = tree[position].son[symbol];
			tree[position].mark += val;
		}
	}
	int find(int str) {
		int position = root, w = 0;
		for (int i = 29; i >= 0; i--) {
			int symbol = (str >> i) & 1;
			if (!tree[position].son[symbol] || !tree[tree[position].son[symbol]].mark) symbol ^= 1;
			position = tree[position].son[symbol];//迭代寻找
			w |= (symbol<< i);
		}
		return w;
	}
}T[2];
int n;
void Read() {
	T[0].init();
	T[1].init();
	scanf("%d", &n);
	int x;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &x);
		T[0].insert(x, 1);
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", &x);
		T[1].insert(x, 1);
	}
}
pii stack[maxn * 2];
int top = 0;
vector<int> res;
void slove() {
	res.clear(); 
	top = 0;
	int sum = 2 * n;
	while (sum) {
		if (!top) {
			stack[++top] = pii(T[0].find(1), 0);
			continue;
		}
		int symbol = T[stack[top].second ^ 1].find(stack[top].first);
		if (top == 1 || stack[top - 1].first != symbol)
			stack[top + 1] = pii(symbol, stack[top].second ^ 1),
			top++;
		else {
			res.push_back(stack[top].first ^ stack[top - 1].first);
			T[stack[top].second].insert(stack[top].first, -1);
			T[stack[top - 1].second].insert(stack[top - 1].first, -1);
			sum -= 2, top -= 2;
		}
	}
}
int main() {
	int t; scanf("%d", &t);
	while (t--) {
		Read();
		slove();
		sort(res.begin(), res.end());
		for (int i = 0; i < res.size(); i++)
			printf("%d%c", res[i], i == res.size() - 1 ? '\n' : ' ');
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值